JavaScript中的this關(guān)鍵字

2018-06-16 20:17 更新

“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ì)象和boatsize屬性值是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ì)象。

總結(jié)

希望今天的文章已經(jīng)澄清了“this”關(guān)鍵字的誤解,并且你總能知道“this”的正確值?,F(xiàn)在我們知道“this”的值不是靜態(tài)的,值得確定依賴于函數(shù)被如何調(diào)用。

原文 http://davidshariff.com/blog/javascript-this-keyword/

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)