function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
昨天在某公众号上看到下面这么一道题,绕来绕去,但是如果基础不够扎实,也很容易掉坑里,像我刚拿到手写了一下答案,再跑一下,发现自己还是有点知识点有所遗漏,所以开一篇来逐步分析,以加强理解
题目
这一题咋一看,说实话,单词就这么几个,但考察的知识点却很多:全局属性,变量提升,原型属性,继承,函数声明等等
Foo.getName();
Foo 是一个函数,因为后续有给函数声明了一个静态属性 Foo.getName,所以其执行结果就是 alert(2),这里要注意的是,正常情况下函数外部是无法访问到函数内部的属性和方法(这属性私有属性和方法),所以这里不会是 alert(1). 另外有些人可能会被 Foo.prototype.getName 这里给误导了,纠结其会不会覆盖上面的属性,实际上这里声明的是公共方法/属性,通过 Foo.getName 是无法访问到的,除非声明在 proto 上面,实际结果可以自己测试一下。
getName();
结果 alert(4) 这里直接调用 getName 方法,前面没有对象,因些是全局调用,因此我们只需要关心声明在 window 下的 getName 方法,如下
这里考察的是 var 关键字的声明提升和函数声明的优先级
所以这里经过浏览器的预处理,变成
alert(5) 会被提升到最高,然后才到 var 关键字声明,再赋值,所以 alert(4) 会覆盖 alert(5)
拓展
这里可以看到,如果函数更高优先级,那么 var 再声明的话, foo 是不是应该是 undefined ?
事实并不是
Foo().getName();
结果
说实话,这里我一开始也是做错了,我认为是 alert(4),当时没发现,Foo 函数里面的 getName 是不带 var 关键字的,所以是相当于覆盖全局的 getName,return this 这里的 this 指的是全局 window。(更多关于this 的介绍,可以查看《你不知道的 JavaScript 上卷》,里面用了两个章节去说明,很详细)
getName();
这里跟之前的一个调用一样,但是因为上面一个执行,导致全局的 getName 被覆盖了,所以输出跟上面一样
new Foo.getName();
结果
以 Foo.getName 作为构造函数实例化,前面我们知道 Foo.getName 是什么,所以 new 就直接把它执行一遍了
new Foo().getName();
结果
这里比上面的多了一个括号,如果不知道 new 运算符的优先级的话,这里很可能就答不对,实际上是 (new Foo()).getName() 这样的运算过程,具体可以看这篇 new与属性访问的顺序 这可以看出,先实例化 Foo,然后取其原型上的 getName 方法
new new Foo().getName();
结果
其实觉得这题有点BT,为了考而考吧 = =|| ,直接抄答案吧,还是考察优先级顺序的问题 new ((new Foo()).getName)();