一道日常的面試題被鄙視引發的較真o( ̄ヘ ̄o#)

原因

開通文章是爲了可以有個地方長篇大論今天遇到的問題
因爲提了一個問題(見 這裏),被人嘲諷。可是這個嘲諷個人人(@xiaoboost )的答案並不對,他的答案只是根據結果解釋可以得出這個結果的執行。至於爲何以及JavaScript在執行過程當中進行了哪些判斷並無進行詳細的解釋或者說完整的解釋。其他的幾個朋友要麼鄙視這道題、要麼鄙視我把運算符優先級牽扯進來。最讓我納悶的是你們都認爲這裏面不牽扯到運算符的優先級判斷。
在此,我以本身的理解來解釋一下JavaScript在執行過程當中進行了哪些判斷和操做。javascript

原始問題:

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()

個人答案:

首先,JS中會對變量聲明和函數聲明在編譯階段進行提高,因此實際代碼會表現成這樣:html

// 此處是變量提高的演示,是在編譯階段進行的
var getName;
function Foo() {
  getName = function () {
    alert(1);
  };
  return this;
}
function getName() {
  alert(5);
}

// 此處則只有到了執行階段纔會執行
Foo.getName = function () {
  alert(2);
};
Foo.prototype.getName = function () {
  alert(3);
};
getName = function () {
  alert(4);
};

其次,除了第六問和第七問,全部有成員訪問運算符(也叫屬性訪問器).的表達式,.的優先級都是最高,沒有一個表達式有圓括號,有圓括號的要麼是函數調用,要麼是new 運算(對象建立表達式)的組成部分。java

這裏專門說明沒有圓括號是由於看到這個問題原做者本身的分析文章將new Foo().getName()中的new Foo()先執行解釋爲因爲圓括號的優先級高於.。附上問題原做者本身的分析文章:http://www.cnblogs.com/xxcang...segmentfault

如下爲各問題的數字輸出以及爲何:函數

第一問

2,直接調用Foo函數的getName方法。this

第二問

4,變量聲明和函數聲明會在編譯階段被提高。此時,函數聲明會覆蓋變量聲明。可是到了執行階段,若是變量有賦值操做,那麼變量會因賦值而覆蓋以前的函數聲明。所以第二問的getName()實際執行的是賦值了匿名函數function () {alert(4)}的函數表達式。prototype

第三問

1,.運算符優先級最高,按照.運算符的關聯性從左往右先計算左操做數,Foo()是一個函數調用表達式,函數內部在執行時,因爲函數內部沒有查找到局部變量getName,所以引擎會沿着做用域鏈向上查找getName變量。在全局做用域中找到getName變量(同時也是window的屬性/方法),給它賦值一個新的匿名函數function(){alert(1)}returnthis此時指向當前方法所屬的對象window,所以,計算右操做數時,就是調用了window對象上的getName方法。而前面左操做數Foo()中已經給getName變量從新賦值了一個匿名函數,所以會出現新賦值函數所彈出的數字1。code

第四問

1,getName()已經由於前面的Foo()的調用而賦值了新的匿名函數,所以彈出數字1。htm

第五問

2,new能夠與Foo結合組成一個沒有參數的對象建立表達式,可是這樣它的優先級低於'.',所以這裏.運算符優先級高於new和函數調用(),整個表達式則能夠理解爲:new (Foo.getName)(),當Foo.getName執行完畢會返回一個匿名函數function(){alert(2)},此時會與new運算符、()組成一個新的表達式:new function(){alert(2)}(),這是一個對象建立表達式,這個表達式在執行執行的時候,構造函數部分function(){alert(2)}會被執行,結果就是2。另外,對象建立表達式在沒有傳入參數的狀況下能夠省略括號,所以原題中的new Foo.getName();能夠把後面的括號省略掉new Foo.getName,結果同樣。對象

第六問

3,帶有參數的對象建立表達式(new constructor())和成員訪問表達式的優先級是同樣,這裏就牽扯到應該理解成
(new Foo()).getName()仍是new (Foo().getName)()。若是是new (Foo().getName)(),那麼在執行Foo()時,它是一個函數調用,優先級低於帶參數的new,所以這樣不行。那麼只能是(new Foo()).getName()new Foo()實例化一個對象,而後經過.訪問getName屬性,對象自己沒有這個屬性,順着原型鏈查找到Foo.prototype中有這個屬性,而且賦值了一個匿名函數。最後經過函數調用運算符調用它,獲得3。

第七問

3,能夠看作 new((new Foo()).getName)()(new Foo()).getName同上面的結果同樣獲得Foo.prototype.getName一個匿名函數:function () {alert(3)},這個匿名函數與前面的new以及後面的()組成一個新的對象建立表達式new function () {alert(3)} (),這個表達式在執行時,會調用其中的構造函數function (){alert(3)},所以會彈出3。

以上是我對這個問題的解釋,其中第六問獲得@zonxin 的解答,再次感謝,附上地址:https://segmentfault.com/q/10...

最後,附上我被嘲諷而且被4我的'踩'的問題地址,若是以爲個人解答是正確的,麻煩幫我'平反'╥﹏╥...;若是有錯誤的地方,請留言指出,我會十分感謝。
個人原問題地址:https://segmentfault.com/q/10...

相關文章
相關標籤/搜索