1.此題涉及的知識點眾多,包括變數定義提升、this指針指向、運算符優先順序、原型、繼承、全局變數污染、對象屬性及原型屬性優先順序等等。 function Foo() { getName = function () { console.log(1) }; return this; } Foo.getNa ...
1.此題涉及的知識點眾多,包括變數定義提升、this指針指向、運算符優先順序、原型、繼承、全局變數污染、對象屬性及原型屬性優先順序等等。 function Foo() { getName = function () { console.log(1) }; return this; } Foo.getName = function () { console.log(2) }; Foo.prototype.getName = function () { console.log(3) }; 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();
答案如下:
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3
解析:先看此題,1.首先創建了一個Foo的函數,2.之後又為Foo創建了一個叫getName的靜態屬性存儲了一個匿名函數,3.之後為Foo的原型創建了一個名叫getName的匿名函數。4.之後又通過函數變數表達式創建了一個getName的函數,5.最後再聲明一個叫getName函。
第一問:自然是訪問Foo上的靜態屬性,是2.
第二問:此處有兩個坑,一是變數聲明提升,二是函數表達式。
以上問題代碼可以等價於:
function Foo() { getName = function () { console.log(1) }; return this; } function getName() { console.log(5) };//先提升函數聲明 var getName;//然後提升變數,不會賦值 Foo.getName = function () { console.log(2) }; Foo.prototype.getName = function () { console.log(3) }; getName = function () { console.log(4) }; 最後執行getName()時自然輸出的是4. 第三問:先執行Foo(),首先在Foo函數的作用域里尋找變數getName,沒有找到就到全局作用域里去尋找,找到了把getName重新賦值了一個匿名函數,最後返回的是window,相當於執行window.getName(),自然結果是1; 第四問:此時已經把變數getName存儲了function () { console.log(1) }的匿名函數。所以結果為1; 後面的就牽扯到運算,優先順序從高到底排序的,可以參考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence![](http://images2017.cnblogs.com/blog/1215688/201711/1215688-20171119150849671-819260058.jpg)
第五問:點.的運算符高於new所以相當於new (Foo.getName());訪問對象Foo的靜態屬性getName(),實際上把getName當作構造函數,結果為2; 第六問: new Foo().getName(),括弧()的優先順序高於new, 相當於(new Foo()).getName();
構造函數的返回值
在傳統語言中,構造函數不應該有返回值,實際執行的返回值就是此構造函數的實例化對象。
而在js中構造函數可以有返回值也可以沒有。
1、沒有返回值則按照其他語言一樣返回實例化對象。
2、若有返回值則檢查其返回值是否為引用類型。如果是非引用類型,如基本類型(string,number,boolean,null,undefined)則與無返回值相同,實際返回其實例化對象。
3、若返回值是引用類型,則實際返回值為這個引用類型。
原題中,返回的是this,而this在構造函數中本來就代表當前實例化對象,遂最終Foo函數返回實例化對象。
之後調用實例化對象的getName函數,因為在Foo構造函數中沒有為實例化對象添加任何屬性,遂到當前對象的原型對象(prototype)中尋找getName,找到了。
遂最終輸出3。
第七問: new new Foo().getName(),相當於new ((new Foo()).getName)();先初始化Foo的實例化對象,然後將其原型上的getName函數作為構造函數再次new; 參考:http://mp.weixin.qq.com/s/eO5hL0pcEHYr7jAbWTWAXg