參考書籍:《JavaScript設(shè)計模式與開發(fā)實踐》;
《JavaScript高級程序設(shè)計(第3版)》(以下簡稱《高級程序設(shè)計》)
JS的this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window媒楼,而當(dāng)函數(shù)被某個對象的方法調(diào)用時,this等于那個對象深纲。
除去不常用的with和eval的情況,具體到實際應(yīng)用中 劲妙,this的指向大致可以分為以下4種(其中還包含一些特殊情況需要被提及)湃鹊。
1、作為對象的方法調(diào)用
//作為對象調(diào)用時镣奋,this指向這個對象
var obj = {
getA : function(){
alert ( this === obj );
}
}
obj.getA(); // true
2币呵、作為普通函數(shù)調(diào)用
當(dāng)函數(shù)不作為對象的屬性調(diào)用時,也就是常說的用普通函數(shù)方式調(diào)用,此時的this總是指向全局對象余赢。在瀏覽器中芯义,這個全局對象是window對象。
window.name = 'globalName';
var getName = function(){
return this.name;
}
console.log( getName() ); // globalName
上面這個例子直接創(chuàng)建了一個函數(shù)并在全局調(diào)用妻柒,事實上扛拨,還可以將對象中的函數(shù)賦值給全局中的函數(shù),請看如下代碼:
//與上一段代碼效果相同
window.name = 'globalName';
var obj = {
name : 'sven',
getName : function(){
return this.name;
}
}
var getName = obj.getName;
console.log( getName() ); // globalName
神奇的是举塔,如果將對象中的函數(shù)賦值給對象中的函數(shù)绑警,this還是會指向全局:
var name = "globalName";
var obj = {
name: "sven",
getName: function() {
return this.name;
}
};
console.log((obj.getName = obj.getName)()); // globalName (非嚴(yán)格模式下)
這是在《高級程序設(shè)計》第183頁的一個例子。書本中的解釋為:(函數(shù)調(diào)用時的)這行代碼先執(zhí)行了一條賦值語句央渣,然后再調(diào)用賦值后的結(jié)果计盒。因為這個賦值表達(dá)式的值是函數(shù)本身,所以this的值不能得到維持芽丹,結(jié)果就返回了"globalName"北启。要詳細(xì)解釋這個問題,網(wǎng)上有一篇文章可以參考:《 this在方法賦值過程中無法保持(隱式丟失)》
簡單點想拔第,以上這些都可看作普通函數(shù)在全局中調(diào)用暖庄,this自然是指向全局的。這也充分說明了之前提到的:JS的this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的楼肪,與函數(shù)聲明時的環(huán)境無關(guān)。
除此之外惹悄,常常會出現(xiàn)函數(shù)內(nèi)部嵌套函數(shù)的情況春叫,匿名函數(shù)作為內(nèi)部函數(shù)很常見。
關(guān)于匿名函數(shù)泣港,《高級程序設(shè)計》中說暂殖,“匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其this對象通常指向window”当纱。然而經(jīng)過測試發(fā)現(xiàn)呛每,函數(shù)內(nèi)部的非匿名函數(shù)也是指向window的:
var name ="Window";
var obj={};
obj.Outer = function(){
function Inner(){
console.log(this.name); //"Window" (非嚴(yán)格模式);"undefined"(嚴(yán)格模式)
}
Inner();
}
obj.Outer();
內(nèi)部函數(shù)在搜索this時,只會搜索到其活動對象為止坡氯,而其活動對象位于作用域鏈的前端晨横,因此永遠(yuǎn)不能直接訪問外部函數(shù)中的this,而是將它指向了全局對象箫柳。若是內(nèi)部函數(shù)需要訪問外部this手形,通常會這樣處理:
var name ="Window";
var obj={
name : "outerName"
};
obj.Outer = function(){
var that = this;
function Inner(){
console.log(that.name); // outerName
}
Inner();
}
obj.Outer();
3、構(gòu)造器調(diào)用
在js中悯恍,可以使用new運算符調(diào)用函數(shù)库糠,此時的函數(shù)被當(dāng)作一個構(gòu)造函數(shù),而構(gòu)造器中的this就指向返回的對象:
var MyClass = function(){
this.name = 'sven';
}
var obj = new MyClass();
console.log( obj.name ); // 輸出:sven
如果構(gòu)造器顯式地返回一個object類型對象涮毫,則運算結(jié)果為最終返回的對象:
var MyClass = function(){
this.name = 'sven';
return {
name: 'anne'
}
}
var obj = new MyClass();
console.log( obj.name ); // 輸出:anne
若是返回非對象類型的數(shù)據(jù)瞬欧,就不會造成上述問題:
var MyClass = function(){
this.name = 'sven' ;
return 'anne' ;
}
var obj = new MyClass();
console.log( obj.name ); // 輸出:sven
4贷屎、call、apply調(diào)用
call艘虎、apply可以動態(tài)改變傳入函數(shù)的this:
var obj1 = {
name: 'sven',
getName: function(){
return this.name;
}
};
var obj2 = {
name: 'anne'
}
console.log( obj1.getName.call(obj2) ); // 輸出:anne
call唉侄、apply這兩個函數(shù)很強大,它擴(kuò)充了函數(shù)賴以運行的作用域顷帖,實現(xiàn)了對象和方法的解耦美旧。call 和 apply 方法能很好地體現(xiàn) JavsScript 的函數(shù)式語言特性,在 JavsScript 中贬墩,幾乎每一次編寫函數(shù)式語言語言風(fēng)格的代碼都離不開call和apply榴嗅。常用的js原型繼承模式,以及js諸多版本的設(shè)計模式中陶舞,都用到了它們嗽测。