“this”關鍵字是JavaScript中廣泛應用的一種特性,但它經常也是這門語言中最容易混淆和誤解的特性?!皌his”的實際意義是什么?它是如何求值的?
本文試圖以清晰的方式澄清和解釋這問題的答案。
有過其他編程經驗的人對“this”關鍵字并不陌生,大部分時候當通過構造函數實例化一個類的實例時,它指新創(chuàng)建的對象。例如,如果我有一個類Boat()
,其擁有一個moveBoat()
方法,當在moveBoat
方法中引用“this”的時候,我們實際上訪問的基于Boat類新創(chuàng)建的對象。
在JavaScript中,當通過“new”關鍵字調用構造函數時,我們也有this概念,然而,這并不是唯一的規(guī)則,并且“this”經常在不同的執(zhí)行上下文中引用到不同的對象。如果你不熟悉JavaScript中的執(zhí)行上下文,我建議你閱讀我的另一篇文章。說的夠多了,讓我們看一些JavaScript的例子:
// 全局作用域
foo = 'abc';
alert(foo); // abc
this.foo = 'def';
alert(foo); // def
每當你在全局作用域中使用“this”關鍵字時(沒在函數內部),它通常指向全局對象(global object)?,F在讓我們看看函數內部“this”的值:
var boat = {
size: 'normal',
boatInfo: function() {
alert(this === boat);
alert(this.size);
}
};
boat.boatInfo(); // true, 'normal'
var bigBoat = {
size: 'big'
};
bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // false, 'big'
那么上面的“this”如何確定?我們看到上面的boat對象有一個size
屬性和一個boatInfo
方法。在boatInfo()
內部,會彈出this的值是否是boat
對象,也會彈出this的size屬性。所以我們執(zhí)行boat.boatInfo()
,我們看見this的值是boat
對象和boat
的size
屬性值是normal
。
然后我們創(chuàng)建另一個對象bigBoat
,也有一個size屬性是big。然而,bigBoat對象沒有boatInfo方法,所以我們從boat對象拷貝方法 bigBoat.boatInfo = boat.boatInfo
。現在,當我們調用bigBoat.boatInfo()并進入函數時,我們看到this不等于boat,并且現在size屬性值是big。為什么會這樣?boatInfo()內部的this值是如何改變的?
你必須意識到的第一件事是函數內部this的值不是靜態(tài)的,每次你調用一個函數它總是重新求值,但這一過程發(fā)生在函數代碼實際執(zhí)行之前。函數內部的this值實際由函數被調用的父作用域提供,更重要的是,依賴實際函數的語法。
當函數被調用時,我們看緊鄰括號“()”的左邊。如果在括號的左側存在一個引用,傳遞給調用函數的“this”值是引用屬于的對象,否則this的值將是全局對象。讓我們看一個例子:
function bar() {
alert(this);
}
bar(); // global - 因為bar方法被調用時屬于 global 對象
var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因為baz()方法被調用時術語foo對象
如果this就這么簡單,那上面的代碼就足夠了。我們可以進一步使事情變得復雜,通過不同的調用語法,改變相同函數內部“this”的值。
var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因為baz被調用時屬于foo對象
var anotherBaz = foo.baz;
anotherBaz(); // global - 因為anotherBaz()被調用時術語global對象
我們看到baz()內部的“this”值每次都不同,這是因為調用的語法不同?,F在,讓我們看看深度嵌套對象內部“this”的值:
var anum = 0;
var foo = {
anum: 10,
baz: {
anum: 20,
bar: function() {
console.log(this.anum);
}
}
}
foo.baz.bar(); // 20 - 因為()的左邊是bar,而它被調用時屬于baz對象
var hello = foo.baz.bar;
hello(); // 0 - 因為()的左邊是hello,而它被調用時屬于global對象
另一個經常被問的問題是事件處理程序內部的“this”關鍵字如何求值?答案是事件處理程序內部的“this”總是引用觸發(fā)事件的元素。讓我們看一個例子:
<div id="test">I am an element with id #test</div>
function doAlert() {
alert(this.innerHTML);
}
doAlert(); // undefined
var myElem = document.getElementById('test');
myElem.onclick = doAlert;
alert(myElem.onclick === doAlert); // true
myElem.onclick(); // I am an element
我們看到當doAlert()第一次調用時,彈出的值是undefined,由于doAlert()屬于global對象。然后我們寫myElem.onclick = doAlert
。這意味這當onclick被出發(fā)時,它作為myElem的一個方法,“this”的值將是myElem元素。
我想說的最后一點是,“this”的值也可以通過call和apply手動設置,這超過我們所討論的范圍。還感興趣的是,當調用構造函數時,“this”引用新創(chuàng)建的實例對象。原因是因為構造函數前面的“new”關鍵字,它創(chuàng)建一個新對象,構造函數內部的“this”總引用新創(chuàng)建的對象。
希望今天的文章已經澄清了“this”關鍵字的誤解,并且你總能知道“this”的正確值?,F在我們知道“this”的值不是靜態(tài)的,值得確定依賴于函數被如何調用。
原文 http://davidshariff.com/blog/javascript-this-keyword/
更多建議: