觉得自己的 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
绑定规则和原型链有全面的理解。通过这类问题的深入分析,不仅可以提升代码阅读能力,也能更自信地处理实际开发中的复杂场景。