說明:這是一道比較典型的綜合考察js中作用域、this指向、對象、解析順序、運算符優(yōu)先級等概念的綜合性考題;話不多說,先上題;
原作者解析:小小滄海(博客園)
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);
}
//請寫出以下輸出結(jié)果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
解析:
1、Foo.getName(); //2
1)結(jié)果執(zhí)行的是Foo對象的一個叫做getName()的屬性,而1、4、5中的getName都是作為函數(shù)存在,所以可以排除1、4、5
2)剩下兩個中,2是Foo對象自身的屬性,3是Foo對象原型鏈上的屬性,而自身屬性的優(yōu)先級高于原型鏈上的屬性,所以執(zhí)行結(jié)果是2
2、getName(); //4
1)結(jié)果執(zhí)行的是getName函數(shù),而題目代碼中有3個相關(guān)函數(shù),分別是1、4、5
2)1中的getName是定義在Foo函數(shù)中的函數(shù),由于Foo尚未執(zhí)行,因此它沒有暴露出來,無法被外部調(diào)用,可以排除
3)4和5都可以被正常調(diào)用,關(guān)鍵在調(diào)用先后問題
4)由于5是普通函數(shù)(優(yōu)先級最高),4是匿名函數(shù);js解析時會將5提前至最上方優(yōu)先解析,而后面解析的4會將5覆蓋,所以執(zhí)行結(jié)果是4
3、Foo().getName(); //1
1)結(jié)果執(zhí)行的是Foo函數(shù),F(xiàn)oo函數(shù)中有個返回值是this;this被普通函數(shù)調(diào)用后,指向的對象一定是window對象,所以此處的結(jié)果已經(jīng)可以解析為window.getName(),即調(diào)用getName()函數(shù)
2)由于window.getName()已經(jīng)被修改為1,所以執(zhí)行結(jié)果是1(??)
上面那句話是原作者的解釋,此處還沒理解透徹為什么1沒有被2覆蓋;下面舉個例子對比下:
//第一個例子是題目的類型,不明為何上邊f(xié)n1的結(jié)果未被覆蓋??
function fn1 (){
a = 1;
return this
}
fn1()
var a = 2
console.log(fn1().a) //1
//第二個例子是我理解的會正常出現(xiàn)覆蓋的情況
function fn1 (){
a = 1;
}
fn1()
var a = 2
console.log(a) //2
4、getName(); //1
1)執(zhí)行g(shù)etName即是執(zhí)行window.getName;所以執(zhí)行結(jié)果同上題是1
5、new Foo.getName(); //2
1)此處考察到了運算符優(yōu)先級的問題,就題目所需來看,成員訪問".">new(帶參數(shù))>函數(shù)調(diào)用"()"(注意,"()"分為函數(shù)調(diào)用及優(yōu)先運算兩種,優(yōu)先級是不同的;原作者開始的解釋就出現(xiàn)了錯誤)
2)結(jié)果先執(zhí)行Foo.getName(),結(jié)果同第一題為2;而new 2不會有任何變化,因此這里的結(jié)果也是2
6、new Foo().getName(); //3
1)從結(jié)果來看,應(yīng)該理解成(new Foo()).getName()這樣執(zhí)行
2)根據(jù)成員訪問".">new(帶參數(shù))>函數(shù)調(diào)用"()";成員訪問優(yōu)先執(zhí)行,右側(cè)是.getName()沒問題
3)左側(cè)分為兩種可能:一種是new(帶參數(shù)),即(new Foo());此處結(jié)果是構(gòu)建一個函數(shù)
4)另一種是先執(zhí)行Foo()函數(shù)后再將其結(jié)果new;
5)因為new(帶參數(shù))>函數(shù)調(diào)用"()",所以是執(zhí)行的(new Foo()),此對象Foo()自身沒有g(shù)etName這個屬性,所以會向上追溯其原型鏈上的屬性,即在此處執(zhí)行了3;因此結(jié)果是3
//這里就是模擬new Foo()的結(jié)果
function F(){return this}
new F() //F {}
關(guān)于這點,原作者有解釋如下(還是可以理解的):
構(gòu)造函數(shù)的返回值
在傳統(tǒng)語言中,構(gòu)造函數(shù)不應(yīng)該有返回值,實際執(zhí)行的返回值就是此構(gòu)造函數(shù)的實例化對象。
而在js中構(gòu)造函數(shù)可以有返回值也可以沒有。
1、沒有返回值則按照其他語言一樣返回實例化對象。
2、若有返回值則檢查其返回值是否為引用類型。如果是非引用類型,如基本類型(string,number,boolean,null,undefined)則與無返回值相同,實際返回其實例化對象。
3、若返回值是引用類型,則實際返回值為這個引用類型。
原題中,返回的是this,而this在構(gòu)造函數(shù)中本來就代表當(dāng)前實例化對象,遂最終Foo函數(shù)返回實例化對象。
7、new new Foo().getName(); //3
1)這題我其實是懵逼的(懵逼臉??)
2)求高手指教了 _ (:з」∠)_
還是貼下原作者解釋,感覺有點不對
第七問, new new Foo().getName(); 同樣是運算符優(yōu)先級問題。
最終實際執(zhí)行為:
new ((new Foo()).getName)();
先初始化Foo的實例化對象,然后將其原型上的getName函數(shù)作為構(gòu)造函數(shù)再次new。
遂最終結(jié)果為3
最后,引用一張原作者提供的相當(dāng)實用的運算符優(yōu)先級表格(鼓掌)



