觉得自己的 JavaScript 功底还不错?那来试试这道复杂的面试题吧!
下面是一段代码,请分析每一行的输出,并解释其背后的原因。
问题描述
以下是代码,预测输出并说明逻辑:
function Foo() {
this.value = 42;
}
Foo.prototype.getValue = function() {
return this.value;
};
const obj1 = new Foo();
const obj2 = {
value: 24,
getValue: obj1.getValue
};
console.log(obj1.getValue()); // A
console.log(obj2.getValue()); // B
setTimeout(function() {
console.log(obj1.getValue()); // C
obj2.value = 100;
console.log(obj2.getValue()); // D
}, 0);
Promise.resolve().then(() => {
obj1.value = 84;
console.log(obj1.getValue()); // E
});
console.log(obj1.getValue()); // F
分析与输出
A:obj1.getValue()
console.log(obj1.getValue()); // A
解释:
- obj1 是 Foo 的实例,obj1.getValue() 调用的是原型上的 getValue 方法。
- 方法中的 this 指向 obj1,返回 this.value。
- obj1.value 初始化为 42,因此输出:
输出:42
B:obj2.getValue()
console.log(obj2.getValue()); // B
解释:
- obj2.getValue 是直接引用了 obj1.getValue,但调用时通过 obj2.getValue()。
- 在 JavaScript 中,this 的绑定依赖调用的对象。在这里,this 指向 obj2。
- obj2.value 为 24,因此输出:
输出:24
F:同步执行的 obj1.getValue()
console.log(obj1.getValue()); // F
解释:
- 此处仍是 obj1.getValue() 的调用,且 obj1.value 尚未被异步代码修改。
- 因此输出和 A 一样,为:
输出:42
E:Promise 中的 obj1.getValue()
Promise.resolve().then(() => {
obj1.value = 84;
console.log(obj1.getValue()); // E
});
解释:
- Promise 的回调是微任务,在同步代码执行完后立即执行。
- 回调中将 obj1.value 修改为 84,随后调用 obj1.getValue()。
- 因此此处返回的是更新后的值:
输出:84
C:setTimeout 中的 obj1.getValue()
setTimeout(function() {
console.log(obj1.getValue()); // C
obj2.value = 100;
console.log(obj2.getValue()); // D
}, 0);
解释:
- setTimeout 的回调是宏任务,在同步代码和微任务都执行完后才会执行。
- 此时,obj1.value 已被微任务修改为 84,调用 obj1.getValue() 返回的是修改后的值:
输出:84
D:setTimeout 中的 obj2.getValue()
console.log(obj2.getValue()); // D
解释:
- 在 setTimeout 的回调中,obj2.value 被修改为 100。
- 调用 obj2.getValue(),this 仍指向 obj2,因此返回的是更新后的值:
输出:100
完整输出顺序
- A:42
- B:24
- F:42
- E:84
- C:84
- D:100
背后的知识点
这道题涉及了 JavaScript 中多个高级概念,是对语言机制的一次全面考察:
原型继承:
- obj1 调用了 Foo 构造函数,通过原型链继承了 getValue 方法。
动态绑定的 this:
- this 的指向取决于函数的调用方式,而不是定义时的上下文。
事件循环与任务队列:
- 同步代码优先执行,Promise 的微任务队列紧随其后,而 setTimeout 的回调则在最后的宏任务队列中执行。
值的动态修改:
- 不同任务(同步、微任务、宏任务)对变量的修改会影响之后的结果。
总结
这道题表面看起来是简单的输出预测,但实际上需要对 JavaScript 的事件循环、this 绑定规则和原型链有全面的理解。通过这类问题的深入分析,不仅可以提升代码阅读能力,也能更自信地处理实际开发中的复杂场景。