前端面试|new new Foo().getName()面试题解析

经典面试题 Foo().getName() 解析
题目如下,写出输出内容(注释:abcde为步骤标号,便于下边分析定位)

// a. function Foo () { getName = function () { console.log(1); } return this; } // b. Foo.getName = function () { console.log(2); } // c. Foo.prototype.getName = function () { console.log(3); } // d. var getName = function () { console.log(4); } // e. function getName () { console.log(5); }Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();

解析:
1. Foo.getName(); // 2
Foo为一个函数对象,对象都可以有属性,题目 b 处定义Foo的getName属性为函数,输出2

2. getName(); // 4
这里看d、e处,d为函数表达式,e为函数声明,两者区别在于变量提升,这两处可以等价于
var getName = undefined; // e处提升到顶部 getName = function () { console.log(5); }... // d. 重新赋值 getName = function () { console.log(4); } // e....

可见函数声明的 5 会被后边函数表达式的 4 覆盖。

3. Foo().getName(); // 1
这里要看a处,在Foo内部将全局的getName重新赋值为 console.log(1) 的函数,执行Foo()返回 this,这个this指向window,Foo().getName() 即为window.getName(),输出 1。

4. getName(); //1
上述3中,全局的getName已经被重新赋值,所以这里依然输出 1。

5. new Foo.getName(); // 2
这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后new一个实例;

6. new Foo().getName(); // 3
【前端面试|new new Foo().getName()面试题解析】这里等价于 (new Foo()).getName(), 先new一个Foo的实例,再执行这个实例的getName方法,但是这个实例本身没有这个方法,所以去原型链__protot__上边找,实例.__protot__ === Foo.prototype,所以输出 3。

7. new new Foo().getName(); // 3
这里等价于new (new Foo().getName()),如上述6,先输出 3,然后new 一个 new Foo().getName() 的实例。
补充:
关于上述 5中 new Foo.getName()先执行 Foo.getName(),而6中 new Foo().getName() 先执行 new Foo(),是因为:
  • new Foo() 属于new(带参数列表)
  • new Foo属于new(无参数列表)
无参数列表的优先级为18,而成员访问的优先级为19,高于无参数列表。因此new Foo.getName()先执行Foo.getName()
带参数列表的优先级为19,而成员访问的优先级也为19,按照运算符规则(同一优先级,按照从左向右的执行顺序),new Foo().getName()先执行new Foo(),再对new之后的实例进行成员访问.getName()操作。
这是js运算符的优先级链接,可查看每个运算符的优先级。


    推荐阅读