阿里经典面向对象面试题升级版
前言
该题目几年前阿里首创,此题一出就制造了面试中"惨案"。该题或许你能说出结果,但你未必能说解析清楚原因。
我查阅了部分关于此题的解析,好多就是迷迷糊糊就给讲完了,根本没抓住问题核心,因此小包系统而整体的讲解一下本题,希望能给大家带来帮助。
下面是原版阿里真题和解析:
题目
function test() {
getName = function () {
Promise.resolve().then(() => console.log(0));
console.log(1);
};
return this;
}
test.getName = function () {
setTimeout(() => console.log(2), 0);
console.log(3);
};
test.prototype.getName = function () {
console.log(4);
};
var getName = function () {
console.log(5);
};
function getName() {
console.log(6);
}
test.getName();
getName();
test().getName();
getName();
new test.getName();
new test().getName();
new new test().getName();
分析
该题目涉及到的知识非常多,比如: 作用域、预编译、原型与原型链、、事件循环。
我们首先来分析一下,该题上半部分到底做了些什么:
与原题目相比,当前题目加入了事件循环的知识, 与 都是异步任务,前者是微任务,后者为宏任务。
解析
预编译
GO: {
test: fn,
getName: function getName() { console.log(6); }
}
执行 函数上的静态方法 ,打印 的回调插入事件队列中,等待同步任务执行完毕
执行之前, 已经被重新赋值为 ,打印
GO: {
test: fn,
// 全局 getName 值修改
getName: function() {
Promise.resolve().then(() => console.log(0))
console.log(1);
};
}
执行全局函数 ,打印 , 压入微任务队列
(⭐)new 知识补充
在进行后面题目的解析时,先补充一点关于 运算符的知识。
首先咱们来看一下 中对 的描述,语法是:
new constructor[([arguments])]
意味着可以缺省,会存在 和 两种模式,并且前者的运算优先级高于后者。更详细的优先级见下图:

从上图可以看到,部分优先级如下:new(带参数列表) = 成员访问 = 函数调用 > new(不带参数列表)
根据上面的知识,表达式中自左向右共有三个运算符: 不带参数列表、成员访问、函数调用。
优先级相同的运算符执行顺序自左向右,因此先执行成员访问,获取到 函数上的静态方法 。
成员访问之后,表达式相当于变为 , 操作符由不带参数列表变为带参数列表,自左向右执行,把 视为构造函数,生成对应实例。
执行,打印 , 回调压入宏任务队列。
表达式运算符自左向右分别为: 带参数列表、成员访问、函数调用
表达式运算符自左向右分别为: 不带参数、 带参数、成员访问、函数调用
因此表达式可以转化为如下形式:
执行微任务队列里的 ,微任务队列中共有两次 ,打印 个
执行宏任务队列里的 回调函数,打印 个
答案
3
5
1
1
3
4
4
0
0
2
2
后语
我是 战场小包 ,一个快速成长中的小前端,希望可以和大家一起进步。
如果喜欢小包,可以在 infoQ 关注我,可以关注,同样可以关注我的小小公众号——小包学前端。
一路加油,冲向未来!!!