JS中this關鍵字很常見,但是它似乎變幻莫測,讓人抓狂。這篇文章就來揭示其中的奧秘。
借助阮一峰老師的話:它代表函數運行時,自動生成的一個內部對象,只能在函數內部使用。這句話看似平常,可是要非常注意三個字:“運行時”,這說明this關鍵字只與函數的執行環境有關,而與聲明環境沒有關系。也就是這個this到底代表的是什么對象要等到函數運行時才知道,有點類似函數定義時的參數列表只在函數調用時才傳入真正的對象。理解了這一點對后面this關鍵字規律的掌握有很大幫助。
this關鍵字雖然會根據環境變化,但是它始終代表的是調用當前函數的那個對象。這就引出了JS中函數調用的問題。在JS中調用函數的模式可以分為4種: 方法調用模式、函數調用模式、構造器調用模式、apply調用模式。這些模式在如何初始化關鍵參數this上存在差異。
一、方法調用模式
當函數被保存為一個對象的屬性時,它就可稱為這個對象的方法。當一個方法被調用時,this被綁定到這個對象上。如果調用表達式包含一個提取屬性的動作(. 或 []),那么它被稱為方法調用。例如:
var name = "window"; var obj = { name: "kxy", sayName: function() { console.log(this.name); } }; obj.sayName(); //kxy
sayName函數作為對象obj的方法調用,所以函數體中的this就代表obj對象。
二、函數調用模式
當一個函數並非一個對象的屬性時,那么它就是被當做函數來調用的。在此種模式下,this被綁定為全局對象,在瀏覽器環境下就是window對象。例如:
var name = "window"; function sayName() { console.log(this.name); } sayName();
sayName以函數調用模式調用,所以函數體中的this代表window對象。
三、構造函數模式
如果在一個函數前面加上new關鍵字來調用,那么就會創建一個連接到該函數的prototype成員的新對象,同時,this會被綁定到這個新對象上。這種情況下,這個函數就可以成為此對象的構造函數。例如:
function Obj() { this.name = "kxy"; } var person = new Obj(); console.log(person.name); //kxy
Obj作為構造函數被調用,函數體內的this被綁定為新創建的對象person。
四、apply調用模式
在JS中,函數也是對象,所有函數對象都有兩個方法:apply和call,這兩個方法可以讓我們構建一個參數數組傳遞給調用函數,也允許我們改變this的值。例如:
var name = "window"; var person = { name: "kxy" }; function sayName() { console.log(this.name); } sayName(); //window sayName.apply(person); //kxy sayName.apply(); //window
當以函數調用模式調用sayName時,this代表window;當用apply模式調用sayName,並給它傳入的第一個參數為person時,this被綁定到person對象上。如果不給apply傳入任何參數,則this代表window。
自此,函數調用的4種模式就都介紹完了,this的綁定規律也就是以上幾種,萬變不離其宗。為了簡單明了的介紹4種模式,以上的例子都比較簡單,那么下面就跟我一起做一個稍復雜的練習,檢驗下自己是否真正掌握了this綁定對象的方法吧!
var name = "window"; function showName() { console.log(this.name); } var person1 = { name: "kxy", sayName: showName } var person2 = { name: "Jake", sayName: function() { var fun = person1.sayName; fun(); } } person1.sayName(); //kxy person2.sayName(); //window
首先心中時刻提醒自己this是在函數執行時被綁定的,不要被任何賦值語句打亂陣腳。
先看第一個執行語句:person1.sayName(); 首先確定這是方法調用模式,對象為person1,再看sayName被賦值為全局函數對象showName,在showName執行時,this綁定的是person1,所以結果為”kxy”。
再看第二個執行語句:person2.sayName(); 這還是方法調用模式,對象為person2,調用的是它的sayName方法。再看sayName函數體,發現函數體最終執行的函數是fun,fun是用函數調用模式調用的。而fun最終也被賦值為showName函數,因為fun是用函數調用模式調用的,所以這里的this綁定為window,結果為”window“。
怎么樣,你做對了嗎?