【譯】javascript的this關鍵詞理解

一直以來,javascript裏邊的this都是一個很難理解的東西,以前看的最多的就是阮一峯老師關於this的理解:javascript

http://www.ruanyifeng.com/blo...html

http://www.ruanyifeng.com/blo...java

今天在留言區發現了一國外大神關於this的理解,藉助翻譯工具讀了一下原文,相對來講是最好的關於理解this的文章,就翻譯了一下,也算是記錄一下。編程

JavaScript的一個經常使用特性是「this」關鍵字,但它也經常是該語言中最使人困惑和誤解的特性之一。「this」究竟是什麼意思?它是如何決定的?小程序

本文試圖澄清這種困惑,並以一種清晰的方式解釋這個問題的答案。app

「this」關鍵字對於那些用其餘語言編程的人來講並不新鮮,並且它一般引用在經過類的構造函數實例化類時建立的新對象。例如,若是我有一個類Boat(),它有一個方法moveBoat(),當在moveBoat()方法中引用「this」時,咱們其實是在訪問新建立的Boat()對象。函數

在JavaScript中,當使用「new」關鍵字調用函數構造函數時,函數構造函數中也有這個概念,可是它不是唯一的規則,並且「this」經常能夠引用來自不一樣執行上下文的不一樣對象。若是您不熟悉JavaScript的執行上下文,我建議您閱讀我關於這個主題的另外一篇文章(本人注:文章找不到了)。談得夠多了,讓咱們來看一些JavaScript例子:工具

// 全局做用域

foo = 'abc';
alert(foo); // abc

this.foo = 'def';
alert(foo); // def

不管什麼時候在全局上下文中使用關鍵字「this」(而不是在函數中),它老是指向全局對象。如今讓咱們看看函數中「this」的值: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()中,若是該對象的值是實際的boat對象,它將發出警報,並警告該對象的size屬性。所以,咱們使用boat.boatInfo()調用函數,能夠看到這是boat對象,而且boat的size屬性是正常的。spa

而後咱們建立另外一個對象bigBoat,它的size屬性爲big。然而,bigBoat對象沒有一個boatInfo()方法,所以咱們使用bigBoat從boat複製該方法。boatInfo = boat.boatInfo。如今,當咱們調用bigBoat.boatInfo()並輸入函數時,咱們看到它不等於boat, size屬性如今是big。爲何會這樣?這個值在boatInfo()中是如何變化的?

您必須意識到的第一件事是,任何函數中的這個值都不是靜態的,它老是在每次調用函數時肯定的,可是在函數實際執行以前,它是代碼。函數內部的值其實是由父做用域提供的,在父做用域中調用函數,更重要的是,函數語法是如何編寫的。

每當調用一個函數時,咱們必須查看方括號/圓括號「()」的左邊。若是在括號的左邊咱們能夠看到一個引用,那麼傳遞給函數調用的「this」的值就是該對象所屬的值,不然它就是全局對象。讓咱們來看一些例子:

function bar() {
    alert(this);
}
bar(); // global - 由於方法bar()在調用時屬於全局對象

var foo = {
    baz: function() {
        alert(this);
    }
}
foo.baz(); // foo - 由於方法baz()在調用時屬於對象foo

若是到目前爲止一切都很清楚,那麼上面的代碼顯然是有意義的。經過用兩種不一樣的方式編寫call / invoke語法,咱們能夠在同一個函數中更改「this」的值,從而使事情變得更加複雜:

var foo = {
    baz: function() {
        alert(this);
    }
}
foo.baz(); // foo - 由於baz在調用時屬於foo對象

var anotherBaz = foo.baz;
anotherBaz(); // global - 由於方法anotherBaz()在調用時屬於全局對象,而不是foo

在這裏,咱們看到baz()中的「this」值每次都是不一樣的,由於它的語法調用有兩種不一樣的方式。如今,讓咱們看看「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,它在調用時屬於全局對象

另外一個常常被問到的問題是如何在事件處理程序中肯定關鍵字「this」?答案是,事件處理程序中的「this」老是指向它所觸發的元素。咱們來看一個例子:

<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()時,它會發出未定義的警報,由於doAlert()屬於全局對象。而後咱們寫myElem。onclick = doAlert,它將函數doAlert()複製到myElem的onclick()事件。這基本上意味着不管什麼時候觸發onclick(),它都是myElem的一個方法,這意味着「This」的值就是myElem對象。

關於這個主題,我想指出的最後一點是,「this」的值也可使用call()和apply()手動設置,覆蓋了咱們今天討論的內容。一樣有趣的是,當在函數構造函數中調用「this」時,「this」引用構造函數中全部實例中新建立的對象。緣由是函數構造函數是用「new」關鍵字調用的,它建立了一個新對象,其中構造函數中的「this」老是引用剛剛建立的新對象。

總結

但願今天的博客文章已經澄清了對「this」這個關鍵字的任何誤解,你能夠一直知道「this」的正確值。咱們如今知道,「this」的值歷來不是靜態的,它的值取決於函數是如何調用的。

原文:http://davidshariff.com/blog/...

歡迎關注小程序,感謝您的支持!

clipboard.png

相關文章
相關標籤/搜索