题目:
function Foo() {
getName = function() {
console.log(1);
};
return this;
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
var getName = function() {
console.log(4);
};
function getName() {
console.log(5);
}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
解析:
首先分析代码上半部分,先定义了一个Foo函数;再Foo函数添加了一个静态属性getName,并且赋值一个匿名函数;接着给Foo函数的原型创建了一个getName的静态属性,并且赋值一个匿名函数;最后分别以函数变量表达式和函数声明的形式定义getName函数。
第一问
Foo.getName():
直接调用Foo上的getName函数,没什么好说的,那就是2.
第二问
getName():
这接调用getName()函数,这里涉及到变量提升的问题,从上面的代码中可以看到getName函数有两种方式定义,一种是函数变量表达式,一种函数声明。并且变量声明会提前。相当于下面的代码:
function Foo() {
getName = function () { console.log(1); };
return this;
}
var getName;//只提升变量声明
function getName() { console.log (5);}//提升函数声明,覆盖var的声明
Foo.getName = function () { console.log (2);};
Foo.prototype.getName = function () { console.log (3);};
getName = function () { console.log (4);};//最终的赋值再次覆盖function getName声明
getName();//最终输出4
最终的getName()还是输出4。
第三问
Foo().getName()
第一步: 执行Foo(),看一看Foo里边有什么:
function Foo() {
getName = function() {
console.log(1);
};
return this;
}
第一句:将一个匿名函数赋值给getName变量,注意的是:getName变量没有用var声明,现在当前Foo函数作用域内找getName变量,没有;接着到该函数作用域的上层找,找到了,即var getName = function () { alert (4);}。 那么执行Foo()时,将全局作用域的getName重新赋值为 function(){console.log(1)},并且返回this这个对象。
第二步:可以理解为this.getName():
关于this的问题,因为这里是直接调用,相当于window.getName(),即这里的this指向window。而第一步执行Foo()时,就将全局的getName()重新赋值,所以最后的结果为1.
第四问
getName():
即直接调用全局的getName(),即于上一问中的一样,返回的是1.
第五问
new Foo.getName():
这里就涉及到运算符的优先级问题了,先看一张图:
可以看到成员访问的优先级大于new运算符,即可以表示为:
new (Foo.getName) (), 可以理解为以Foo.getName为构造函数来执行。这里的Foo.getName()很容易看出来,就是Foo的静态属性。
回顾调用new的过程:
- 新生成了一个对象
- 获得构造函数
- 链接到原型
- 绑定 this,执行构造函数
- 返回新对象
function create() {
// 创建一个空对象
let obj = new Object();
// 获得构造函数, arguments中去除第一个参数(第一个参数为构造函数)
let Con = [].shift.call(arguments);
// 链接到原型
obj.__proto__ = Con.prototype;
// 绑定this,执行构造函数
let result = Con.apply(obj, arguments);
// 优先返回构造函数返回的对象
return typeof result === 'object' ? result : obj;
}
深入理解点击:深入理解new的原理与模拟实现
回到本题,new (Foo.getName) ()返回一个对象,并且执行了Foo.getName的构造函数,所以返回2.
第六问
new Foo().getName(): 即(new Foo()).getName ()。
- 先以Foo为构造函数执行,返回一个对象,再执行这个对象的getName()。
- Foo函数返回一个this,而this在构造函数中本来就代表当前实例化对象,所以new Foo()返回一个实例化对象 。
- 接着执行这个对象的getName方法,通过原型链找到了 Foo 上的 getName 函数,所以结果为 3
第七问
new new Foo().getName(): 即 new( (new Foo()).getName) )()
- 这里就是在第六问的基础上增加了一个new,是以Foo上的getName函数为构造函数,执行并且返回一个对象。
- 最终结果为3
参考链接:一道常被人轻视的前端Js面试题