为什么会花一上午的时间来总结这道题呢?
原因是这样的:最近一直在准备面试的东西,也在B站学习(注意是学习!学习!学习!),看到尚硅谷分享的这道js面试题,当前学到了很多。
昨天晚上接到字节hr的电话,预约下周的一面面试。对于我这个菜鸟来讲,当然是得去牛客网刷刷面经辣(哭脸)
当当当当~~~~眼前一亮(这道题似曾相识啊!!!于是我翻手机相册翻到了截图(本人喜欢将一些知识点截图,蹲厕所的时候刷刷相册。哈哈哈,据说,上厕所时看知识最容易记住呢!)以下是证据(说话做事凭证据~)废话不多说,让我们一起愉快的学习吧。
题目如下:我又修改了以下,添加了两行代码
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 Foo().getName();
(new Foo()).getName();
new new Foo().getName()
首先你要清楚,面试官考察的知识点(预解析,原型链,this)。面试官也是够狠的,区区十几行代码考察这么多知识点!
下面开始梳理解题过程:
预解析结果:(变量和函数声明提升,赋值不提升,再赋值前调用返回undefined,当变量和函数重名时,优先保留函数)
先预解析第一个函数:
function Foo() { getName = function () { alert(1); }; return this; }
碰到:
var getName = function () { alert(4); }; =》将getName声明提前,就剩下 getName = function(){alert(4)}
function Foo() { getName = function () { alert(1); }; return this; } var getName
碰到:
function getName() { alert(5); }
function Foo() { getName = function () { alert(1); }; return this; } var getName; function getName() { alert(5);
以上就是预解析过程,接下来执行代码:
碰到
getName = function(){alert(4)},将预解析中的function getName() { alert(5); 重新赋值
此时代码为:
function Foo() { getName = function () { alert(1); }; return this; } var getName; Foo.getName = function () { alert(2); }; Foo.prototype.getName = function () { alert(3); }; getName = function () { alert(4); };
接下来,执行语句:
第一条语句:
Foo.getName();
找Foo函数找到getName方法 找到Foo.getName = function () { alert(2); }; 打印输出2
第二条语句:
getName();
找到getName = function () { alert(4); }; 打印输出4
第三条语句:
Foo().getName();
首先明白一件事,运算符的优先级(点.的优先级高),但是因为()括号无法.点调用,所以先将Foo函数执行完再去执行.getName()方法
等价于(Foo()).getName(); 先看Foo函数,一个全局变量getName,一个return this,
所以此时的getName再次被重新赋值
function Foo() { getName = function () { alert(1); }; return this; } var getName; Foo.getName = function () { alert(2); }; Foo.prototype.getName = function () { alert(3); }; getName = function () { alert(1); };
Foo返回this,返回的就是调用Foo函数的对象,那么是谁调用了Foo函数呢?很明显是window大哥,所以Foo函数的运行结果为window
所以(Foo()).getName() 等价于window,getName(),好了,你在全局找getName方法吧,getName = function () { alert(1); };,打印输出1
第四条语句:
getName():
等价于window.getName();这不就是和上一步一样吗,getName = function () { alert(1); };,打印输出1
第五条语句:
new Foo.getName()
还是那句话,点.的优先级高,先执行 那么就等价于 new(Foo.getName)(),问题来了,Foo.getName是什么呢?聪明的你应该已经发现了:
Foo.getName = function () { alert(2); };
也就是说 new(Foo.getName)() = new ( function () { alert(2); };){} ,小二又向你无情的抛出了一个知识点:new函数调用时,会执行这个函数,所以打印输出2
第六条语句:
new (Foo.getName)();
你看她像不像第五题呢?,结果和第五条语句一样,输出结果为2
第七条语句:
new Foo().getName();
还是那句话:尽管.的优先级高,但()并不能.调用,所以会将new Foo()的值求出来再去.getName
(new Foo()).getName()-->new关键字最后会生成一个实例对象foo.getName()
实例对象如何去找到对应的属性?(沿着隐式原型链__proto__,先去自身,再去__proto__,直到Object,直到都没有找到就返回undefined)
找到 Foo.prototype.getName = function () { alert(3); }; 打印输出3
第八条语句:
(new Foo()).getName();
你看他像不像第七题呢?结果和第七条语句一样,输出3
第九条语句:
new new Foo().getName()
new ((new Foo()).getName)() => new (foo.getName)()
new (function () { alert(3); };)() ,直接执行new的函数,打印3