一、题目
面试某互联网大厂时遇到这道题,发现原来是一道经典原题,早知道就好好刷真题了,这里整理一下
function Foo() {
getName = function () {
console.log(1);
};
console.log('this is' + this)
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log('baidu' && 'google');
};
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();
console.log('baidu' && 'google');
console.log('baidu' || 'google');
二、分析
1.整体分析
本题涉及到变量提升函数提升以及二者的优先级问题、运算符.、()、new 的优先级问题、原型链方法问题
2.变量提升和函数提升
3.运算符优先级
优先级:成员访问 > new > 函数调用,尽管 . 的优先级高,但 (). 这种情况下,() 并不能被 . 调用,因此,会先将 new Foo() 的值求出来再执行 .,应该不会有同学问为啥不把 new 、Foo、() 拆开来看吧?原因就是 new Foo() 是构造函数执行的方式,不能拆开,不熟悉的同学去补基础!
构造函数方式返回的结果是一个新的实例对象,所以 this 的值不再是调用它的那个。
4.逐句运行
- 解析器走完一遍执行了变量提升和函数提升,先提升 gatName 变量到顶部,此时全局上有个 getName 的属性,值为 undefined, 又因为函数提升优先级比变量提升高,所以此时 getName 又变成了console.log(5) 的那个函数,随着代码的执行,对 getName 变量进行了赋值操作,因此这个变量的值又被改变成了console.log(4)这个函数
- 开始执行 Foo.getName()
是 Foo 函数的静态属性,指向一个匿名函数 - getName()
调用全局上的 getName 函数,这里的 this 就是 windows - Foo().getName()
执行 Foo 函数后,在修改了全局变量 getName, 因为 getName 前没有 var. Foo 里返回的 this 是调用 Foo 函数的this,即 windows,再调用 windows 上的 getName,输出 1 - getName();
调用 windows 上的 getName 函数,输出1 - new Foo.getName();
实际上执行顺序是: new (Foo.getName) ()
访问 Foo 的静态属性指向的函数,输出2 - new Foo().getName();
尽管.的优先级高,但()并不能被.调用,所以会将new Foo()的值求出来再去.getName。(有个小技巧,就是先找 ., 能调用就调用,不能就先执行与它最近的运算符,有new Foo()就一定是执行构造函数,不能拆开成 new Foo 和 (), 或者 new 和 Foo())。new Foo() 返回一个新的实例,即此时 Foo 函数内返回的 this 是指向那个新实例的。再调用这个实例的 getName 方法,就是调用原型链上的方法 - new new Foo().getName();
执行顺序:new ((new Foo()).getName)()