為什么會花一上午的時間來總結這道題呢?
原因是這樣的:最近一直在准備面試的東西,也在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