觉得自己的 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
绑定规则和原型链有全面的理解。通过这类问题的深入分析,不仅可以提升代码阅读能力,也能更自信地处理实际开发中的复杂场景。
首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。