Bootstrap

阿里经典面向对象面试题升级版

前言

该题目几年前阿里首创,此题一出就制造了面试中"惨案"。该题或许你能说出结果,但你未必能说解析清楚原因。

我查阅了部分关于此题的解析,好多就是迷迷糊糊就给讲完了,根本没抓住问题核心,因此小包系统而整体的讲解一下本题,希望能给大家带来帮助。

下面是原版阿里真题和解析:

传送门:

题目

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 关注我,可以关注,同样可以关注我的小小公众号——小包学前端

一路加油,冲向未来!!!