本文記錄一些在網(wǎng)上看到的昔逗,我認為有意思的前端面試題枉长,歡迎轉載,轉載請注明出處:林東洲的博客 | Lindz Blog钢属。
輸出下面程序題的結果:
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();
先來理解下題目,首先創(chuàng)建了一個Foo()函數(shù)门躯,然后在給Foo函數(shù)增添一個函數(shù)屬性getName,在Foo原型上增添一個getName函數(shù)屬性淆党,然后再以函數(shù)表達式和函數(shù)聲明分別創(chuàng)建了一個同名函數(shù)getName。
第一問:Foo.getName()返回2讶凉。
第二問:首先需要知道函數(shù)表達式和函數(shù)聲明的區(qū)別染乌。
函數(shù)聲明具有變量聲明提升(即所有聲明變量或聲明函數(shù)都會被提升到當前函數(shù)的頂部)
例:
console.log('x' in window);
console.log(typeof func);
var x = 0;
function func(){}
js引擎會將其解析為:
var x;
function func(){}
console.log('x' in window); //true
console.log(typeof func); //function
x = 0;
如果能明白的話就能理解下面代碼的輸出:
console.log(x); //function(){}
var x = 1;
console.log(x); //1
function x(){}
console.log(x); //1
所以js引擎在解析代碼時候就變成了:
//省略部分代碼
function Foo() {
getName = function () { alert (1); };
return this;
}
var getName;
function getName() { alert (5);}
getName = function () { alert (4);};
getName(); //4
后面函數(shù)表達式的賦值將前面的getName值個覆蓋了,所以第二問輸出4懂讯。
第三問:首先執(zhí)行Foo()函數(shù)荷憋,并執(zhí)行getName的賦值語句。
它會查找當前作用域上是否存在這個變量褐望,如果沒有就向它上層作用域繼續(xù)尋找勒庄,直到找到window全局作用域,若還是沒有則在全局作用域上創(chuàng)建getName變量瘫里,
因為window域中存在著getName這個變量即(var getName = function () { alert (4);};)实蔽,所以將其替換為(getName = function () { alert (1); };)
并返回window對象,所以Foo().getName()的結果為1谨读;
若代碼改為:
function Foo() {
var getName = function () { alert (1); };
return this;
}
Foo().getName(); //TypeError: Foo().getName is not a function.
創(chuàng)建getName時候多加一個var局装,即在當前作用域中添加一個局部變量getName,而在執(zhí)行完畢后該局部變量就會被回收劳殖,并且該函數(shù)返回window全局對象贼邓,故在訪問window中的getName會報錯,因為沒有創(chuàng)建window對象無此變量闷尿。
第四問:正如上一問塑径,因為全局作用于window中getName被替換。
即(getName = function () { alert (1); };)
故此時getName();輸出結果為1填具。
第五問 new Foo.getName(); ,此處考察的是js的運算符優(yōu)先級問題统舀。
根據(jù)圖中優(yōu)先級的描述:
可以得知:可以得知點(.)的優(yōu)先級高于new操作,所以原式相當于是:
new (Foo.getName)();
所以結果為2劳景;
第六問 new Foo().getName()
根據(jù)表的執(zhí)行結果為:(new Foo()).getName();
所以接下來先討論一下js構造函數(shù)返回值的問題:
js返回值分為三類:
1誉简、沒有返回值則創(chuàng)建一個空對象,并把函數(shù)中與this綁定的屬性添加盟广;
function F(){ this.name = 'foo';}
var obj = new F(); // obj: {name:'foo'} 創(chuàng)建一個對象闷串,并綁定函數(shù)中this所綁定的屬性
2、若有返回值則檢查返回值的類型筋量,若返回值為非引用類型烹吵,比如基本類型:(string,number,boolean,null,undefined)碉熄,則js引擎會忽略該返回值,并且創(chuàng)建一個新對象肋拔,并把函數(shù)中與this綁定的屬性添加锈津;
function F(){ this.name = 'foo'; return true;}
var obj = new F(); // obj: {name:'foo'} 創(chuàng)建一個對象,并綁定函數(shù)中this所綁定的屬性
3凉蜂、若返回值是引用類型琼梆,則實際返回值為這個引用類型。
function F(){this.name = 'foo'; return {x:1};}
var obj = new F(); //obj: {x:1}
如果理解這些窿吩,再來看下面一個例子:
var obj;
function F1(){this.name = 'foo'; return 'a';}
obj = new F1(); //obj: F1{name: 'foo'}
function F2(){this.name = 'foo'; return String('a');}
obj = new F2(); //obj: F2{name: 'foo'}
function F3(){this.name = 'foo'; return new String('a');}
obj = new F3(); //obj: String {0: "a", length: 1}
從例子中我們可以得出:無論 ‘a(chǎn)’還是String('a')創(chuàng)建的都是基本類型值茎杂,而new String('a')則創(chuàng)建一個字符串包裝對象。
原題中纫雁,返回的是this蛉顽,而this在構造函數(shù)中本來就代表當前實例化對象。
之后調(diào)用實例化對象的getName函數(shù)先较,因為在Foo構造函數(shù)中沒有為實例化對象添加任何屬性携冤,遂到當前對象的原型對象(prototype)中尋找getName,故最終輸出3闲勺。
第七問:new new Foo().getName(); 同樣是運算符優(yōu)先級問題曾棕。
執(zhí)行結果為:new ((new Foo()).getName)();
先初始化Foo的實例化對象,然后將其原型上的getName函數(shù)作為構造函數(shù)再次new菜循,故最終結果為3