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