JavaScript中的this關鍵字

2018-06-16 20:17 更新

“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對象和boatsize屬性值是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/

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號