前端实习生必看:从亚信笔试高频错题,拆解CSS3伪类和Vue通信的5个易混点
前端实习生必看:从亚信笔试高频错题拆解CSS3伪类和Vue通信的5个易混点
最近在辅导几位准备前端实习面试的同学时,发现他们在复盘亚信等大厂笔试错题时,总会在CSS3伪类选择和Vue组件通信这几个知识点上反复栽跟头。这让我想起自己当年面试时也曾在这些"陷阱题"上吃过亏。今天我们就来深度剖析这些高频错题背后的原理,帮你建立正确的知识框架。
1. CSS3伪类选择器:你以为的"简单题"可能藏着坑
很多同学觉得CSS选择器是基础中的基础,笔试时看到这类题目往往会掉以轻心。但大厂笔试题最擅长的就是把简单知识点设计成"看起来简单实际暗藏玄机"的题目。让我们看这道经典错题:
下面CSS3新增伪类中不正确的一个是?
A. p:first-of-type
B. p:only-of-child
C. p:nth-child(2)
D. :disabled
正确答案是B ,因为CSS3中根本没有 only-of-child 这个伪类。但为什么这么多人会选错?我们来分析常见误区:
1.1 only-child vs only-of-type:一字之差,天壤之别
这两个伪类特别容易混淆:
| 伪类选择器 | 匹配条件 | 示例说明 |
|---|---|---|
| :only-child | 元素是其父元素的唯一子元素 | <div><p></p></div> 中的p元素 |
| :only-of-type | 元素是其父元素中同类型的唯一元素 | <div><p></p><span></span></div> 中的p元素 |
关键区别:
:only-child要求 绝对唯一 ,不能有任何兄弟节点:only-of-type只要求 同类唯一 ,可以有其他类型的兄弟节点
1.2 高频变式题预测
面试官可能会这样变式考察:
<div>
<p>第一个段落</p>
<span>span元素</span>
<p>第二个段落</p>
</div>
问:如何选择唯一的span元素?答案应该是 span:only-of-type 而不是 span:only-child 。
2. Vue组件通信:props的"单向数据流"陷阱
另一道高频错题涉及Vue组件通信:
关于Vue组件间通信说法错误的是( )
A. Vuex可以实现任何关系的组件间的通信
B. 如果子组件修改通过props获取的父组件传过去的字符串或数字会报错
C. 子组件可以通过$emit给父组件传值
D. 可以通过context进行组件间传值
正确答案是D ,因为context是React的API,Vue中并不存在。但更值得关注的是B选项的深层原理。
2.1 props修改报错的真相
很多同学知道"props不能直接修改"这个规则,但不清楚背后的机制:
// 父组件
<template>
<child-component :message="parentMsg" />
</template>
// 子组件
props: ['message'],
methods: {
updateMessage() {
this.message = '修改props' // 控制台会警告
}
}
原理剖析 :
- Vue使用
Object.defineProperty将props设置为只读属性 - 基本类型(字符串、数字等)修改会直接报错
- 对象/数组等引用类型修改属性不会报错(但违反单向数据流原则)
提示:正确的做法是使用
$emit通知父组件修改,或在子组件中使用局部变量拷贝props值
2.2 Vue通信方案适用场景对比
| 通信方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| props | 父→子通信 | 简单直接 | 层级深时繁琐 |
| $emit | 子→父通信 | 事件驱动 | 需要父组件监听 |
| Vuex | 复杂组件关系/跨级通信 | 集中管理 | 小型项目可能过度设计 |
| 事件总线 | 非父子关系组件 | 灵活 | 难以追踪维护 |
| provide/inject | 祖先→后代组件通信 | 跨层级 | 响应式处理需要额外工作 |
3. this指向:箭头函数与传统函数的差异
这道关于 this 指向的题目错误率也很高:
var p1 = {
name: '小明',
age: '12',
action: function(where, doing) {
console.log(this.age + '岁的' + this.name + '在' + where + doing);
}
}
var p2 = { name: '小红', age: '15' }
console.log(p1.action.call(p2, '操场上', '运动'))
正确答案是D ,但很多同学不理解 call 和箭头函数的区别。
3.1 函数this指向的四种规则
-
默认绑定 :独立函数调用时
this指向全局对象(严格模式下为undefined)function foo() { console.log(this) } foo() // window或global -
隐式绑定 :作为对象方法调用时指向该对象
obj.foo() // this指向obj -
显式绑定 :通过call/apply/bind强制指定this
foo.call(obj) // this强制指向obj -
new绑定 :构造函数中this指向新创建的对象
new Foo() // this指向新实例
3.2 箭头函数的特殊之处
箭头函数没有自己的this,它的this继承自外层函数作用域。这就是为什么在事件监听器中:
function test() {
this.flag = false;
this.change = () => {
this.flag = true;
console.log(button.flag);
};
}
const button = new test();
document.addEventListener("click", button.change);
点击后会输出 true ,因为箭头函数的this始终指向button实例,而不是触发事件的DOM元素。
4. 前端算法基础:广度优先搜索的实现
虽然前端对算法要求相对较低,但大厂笔试中常会考察基础算法实现:
在图的广度优先搜索遍历算法中,使用的辅助数据结构是?
正确答案是队列 ,这与深度优先搜索(使用栈)形成鲜明对比。
4.1 BFS与DFS对比实现
// 广度优先搜索(队列实现)
function BFS(node) {
const queue = [node]
while (queue.length) {
const current = queue.shift()
console.log(current.value)
current.children.forEach(child => queue.push(child))
}
}
// 深度优先搜索(栈实现)
function DFS(node) {
const stack = [node]
while (stack.length) {
const current = stack.pop()
console.log(current.value)
// 注意子节点要逆序入栈
[...current.children].reverse().forEach(child => stack.push(child))
}
}
关键区别:
- BFS按层级遍历,适合最短路径问题
- DFS沿着分支深入,适合存在性判断
5. eval的"性能陷阱"与替代方案
最后一道关于 eval 的题目虽然简单,但背后的知识点很重要:
eval的功能就是将一串字符串转化为JS代码并执行,返回一个或多个值,如果eval中的参数不是字符串,那么函数返回的就是这个参数,而且整个过程非常消耗性能,不推荐使用
5.1 为什么不推荐使用eval?
-
安全风险 :可能执行恶意代码
eval(userInput) // 极度危险! -
性能问题 :无法被JavaScript引擎优化
- 需要启动解释器
- 无法应用JIT编译优化
-
调试困难 :错误堆栈信息不清晰
5.2 安全替代方案
| 场景 | 替代方案 | 示例 |
|---|---|---|
| JSON解析 | JSON.parse() | JSON.parse('{"a":1}') |
| 动态属性访问 | 方括号表示法 | obj[propName] |
| 动态函数生成 | Function构造函数 | new Function('a', 'return a*2') |
在实际项目中,我曾遇到一个案例:开发者使用eval解析后端返回的JSONP数据,结果因为后端被XSS攻击导致前端执行了恶意脚本。改用 JSON.parse 后不仅性能提升30%,还彻底消除了安全隐患。
更多推荐


所有评论(0)