譯者按: JavaScript的this
和Java等面嚮對象語言中的this
大不同,bind()
、call()
和apply()
函數更是將this
的靈活度進一步延伸。javascript
原文: JavaScript: The Keyword ‘This’ for Beginnersjava
譯者: Fundebug小程序
爲了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原做者全部,翻譯僅用於學習。微信小程序
若是對JavaScript的關鍵字this
理解不夠深入,有時候會掉入意想不到的坑。在這裏咱們總結了5條通用規則來幫助你判斷this
到底指向什麼。雖然沒有囊括全部的狀況,但平常大部分狀況均可以使用這些規則來正確推斷。數組
this
的值一般是由所在函數的執行環境決定,也就是說要看函數是如何被調用的;this
均可能指向不一樣的對象;打開Chrome瀏覽器開發者面板(Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J),而且輸入:瀏覽器
console.log(this);
看看輸出了什麼? 微信
// Window {}
window
對象! 由於在全局做用域下,this
指向全局對象。在瀏覽器中全局對象就是window
對象。
爲了讓你更加清楚理解爲何this
會指向window
對象,咱們來看另一個例子:app
var myName = 'Brandon';
咱們能夠經過在控制檯輸入myName
來訪問其值:函數
myName // 輸出 'Brandon'
其實,全部在全局定義的變量都綁定到window
對象。咱們來作以下測試: 學習
window.myName // 輸出 'Brandon' window.myName === myName // 輸出 true
如今咱們將this
放到函數內部,看看有何效果。
function test(){ return this; } test();
聲明的對象 (Declared Object)
你會發現this
依然指向全局的window
對象。由於this
關鍵字沒有處於一個聲明的對象內部,默認指向全局window
對象。這一點可能對於大部分初學者來講有點難以理解。當讀完這篇文章,你就會豁然開朗。
注意:若是在strcit
模式下,上面的例子中this
爲undefined
。
當this
關鍵字在一個聲明對象內部使用,其值會被綁定到調用該this
的函數的最近的父對象。咱們用例子來講明這個問題:
var person = { first: 'John', last: 'Smith', full: function() { console.log(this.first + ' ' + this.last); } }; person.full(); // 輸出 'John Smith'
在被聲明的對象person
中的full
函數裏面使用了this
, 那麼調用this
的full
函數的最近的父對象就是person
, 所以,this
指向person
。
爲了更好的描述this
其實是指向person
對象的,你能夠拷貝以下代碼到瀏覽器控制檯,將this
打印出來。
var person = { first: 'John', last: 'Smith', full: function() { console.log(this); } }; person.full(); // 輸出 Object {first: "John", last: "Smith", full: function}
咱們接下來看一個更復雜的例子:
var person = { first: 'John', last: 'Smith', full: function() { console.log(this.first + ' ' + this.last); }, personTwo: { first: 'Allison', last: 'Jones', full: function() { console.log(this.first + ' ' + this.last); } } };
在這裏咱們有嵌套的對象,在這個時候,this
分別指向誰呢? 咱們將其打印出來看一看:
person.full(); // 輸出 'John Smith' person.personTwo.full(); // 輸出 'Allison Jones'
你會發現知足咱們前面所描述的規則:其值會被綁定到調用this
的函數的最近的父對象。
new
關鍵字當使用new
關鍵字構建一個新的對象,this
會綁定到這個新對象。咱們來看一個例子:
function Car(make, model) { this.make = make; this.model = model; };
根據第一條規則,你可能會推斷this
指向全局對象。可是若是咱們使用new
關鍵字來聲明一個新的變量,Car
函數中的this
將會綁定一個新的空對象,而後初始化this.make
和this.model
的值。
var myCar = new Car('Ford', 'Escape'); console.log(myCar); // 輸出 Car {make: "Ford", model: "Escape"}
call
, bind
, 和apply
咱們能夠顯示的在call()
,bind()
,apply()
中設置this
的綁定對象。這三個函數很相似,可是咱們須要注意它們微小的區別。
咱們來看一個例子:
function add(c, d) { console.log(this.a + this.b + c + d); } add(3,4); // 輸出 NaN
函數輸出
addNaN
, 由於this.a
和this.b
未定義。
如今咱們引入對象,而且使用call()
和apply()
來調用:
function add(c, d) { console.log(this.a + this.b + c + d); } var ten = {a: 1, b: 2}; add.call(ten, 3, 4); // 輸出 10 add.apply(ten, [3,4]); // 輸出 10
當咱們使用add.call()
,第一個參數是this
須要綁定的對象,剩下的是add
函數原本的參數。
所以,this.a
指向ten.a
,this.b
指向ten.b
。add.apply()
也相似,除了第二個參數是一個數組,用於存儲add
函數的參數。
bind()
函數和call()
相似,可是bind()
函數不會當即被調用。bind()
函數會返回一個函數,而且將this
綁定好。接下來咱們來用例子來幫助理解bind()
函數的應用場景:
var small = { a: 1, go: function(b,c,d){ console.log(this.a+b+c+d); } } var large = { a: 100 }
執行:
small.go(2, 3, 4); // 輸出 10
若是咱們想使用large.a
的值,而不是small.a
呢? 咱們可使用call/apply
:
small.go(2, 3, 4); // 輸出 10
可是,若是咱們如今還不知道這三個參數應該傳入什麼值,應該怎麼辦呢? 咱們可使用bind
:
var bindTest = small.go.bind(large, 2);
若是咱們將bindTest
在控制檯下打印出來,咱們會看到:
console.log(bindTest); // 輸出 function (b,c,d){console.log(this.a+b+c+d);}
注意:該函數已經將this
綁定到large
對象,而且傳入了第一個參數b
。因此,咱們接下來是須要傳入餘下的參數便可:
bindTest(3, 4); // 輸出 109
箭頭函數(=>)
由於須要很大的篇幅,咱們會專門寫一篇博客來介紹。
當你讀完這篇博客,你應該能夠理解大多數狀況下this
指向的對象。
接下來咱們來總結一下:
this
的值一般是由當前函數的執行環境所決定;this
指向全局對象 (window
對象);new
關鍵字聲明,this
指向新建對象;call()
, bind()
, apply()
來設置this
;this
。Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了6億+錯誤事件,獲得了Google、360、金山軟件等衆多知名用戶的承認。歡迎免費試用!