JavaScript箭頭函式中的this詳解
前言
箭頭函式極大地簡化了this的取值規則。
普通函式與箭頭函式
普通函式指的是用function定義的函式:
var hello = function () { console.log("Hello, Fundebug!"); }
箭頭函式指的是用=>定義的函式:
var hello = () => { console.log("Hello, Fundebug!"); }
JavaScript箭頭函式與普通函式不只是寫法上的區別,它們還有一些微妙的不同點,其中一個不同點就是this。
箭頭函式沒有自己的this值,箭頭函式中所使用的this來自於函式作用域鏈。
這句話很簡單,不過聽著稍微有點莫名其妙,得從頭說起。
this到底是什麼?
關於this的文章也夠多了,有時候越描越黑,我就不再添亂了,我只負責搬運一下MDN文件:this,感興趣的可以仔細閱讀一下,我摘錄一些最重要的話就好了。
A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
JavaScript是一門比較奇特的語言,它的this與其他語言不一樣,並且它的取值還取決於程式碼是否為嚴格模式("use strict")。
this的值是什麼?
The JavaScript context object in which the current code is executing.
this就是程式碼執行時當前的context object。
Global context
In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.
程式碼沒有在任何函式中執行,而是在全域性作用域中執行時,this的值就是global物件,對於瀏覽器來說,this就是window。
這一條規則還是比較容易接受的。
Function context
Inside a function, the value of this depends on how the function is called.
函式中的this值取決於這個函式是怎樣被呼叫的,這一條規則就有點變態了,也是很容易出BUG的地方。
另外,this的值還與函式是否為嚴格模式("use strict")有關,這就非常的喪心病狂了...
大家如果好奇的話,出門左轉看MDN文件,我多說無益,只說明一種簡單的情況。
As an object method
When a function is called as a method of an object, its this is set to the object the method is called on.
當函式作為物件的方法被呼叫時,它的this值就是該物件。
var circle = { radius: 10, getRadius() { console.log(this.radius); } }; circle.getRadius(); // 列印 10
self = this?
當我們需要在物件方法中巢狀一個內層函式時,this就會給我們帶來實際的困擾了,大家應該寫過這樣的程式碼:
// 使用臨時變數self var circle = { radius: 10, outerDiameter() { var self = this; var innerDiameter = function() { console.log(2 * self.radius); }; innerDiameter(); } }; circle.outerDiameter(); // 列印20
outerDiameter函式是circle物件的方法,因此其this值就是circle物件。
那我們直接寫this.radius多好啊,可惜不能這麼寫,因為內層函式innerDiameter並不會繼承外層函式outerDiameter的this值。outerDiameter函式的this值就是circle物件,this.radius等於10。
但是,innerDiameter函式的this值不是circle物件,那它到底是啥?它是innerDiameter函式執行時當前的context object,這個context object又是啥?其實我也暈了,所以不妨測試一下:
// innerDiameter函式中的this是window var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(this === window); }; innerDiameter(); } }; circle.outerDiameter(); // 列印true
innerDiameter函式中的this是window,為啥是window這個不去管它,反正不是circle物件。
因此,如果我們直接在innerDiameter函式中使用this的話,就出問題了:
// 使用普通函式 var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(2 * this.radius); }; innerDiameter(); } }; circle.outerDiameter(); // 列印NaN
於是,我們不得不使用一個臨時變數self將外層函式outerDiameter的this值搬運到內層函式innerDiameter。
.bind(this)
我們也可以使用.bind(this)來規避this變來變去的問題:
// 使用.bind(this) var circle = { radius: 10, outerDiameter() { var innerDiameter = function() { console.log(2 * this.radius); }; innerDiameter = innerDiameter.bind(this); innerDiameter(); } }; circle.outerDiameter(); // 列印20
但是,無論是使用臨時變數self,還是使用.bind(this),都不是什麼很簡潔的方式。
總之,普通函式的this取值多少有點奇怪,尤其當我們採用面向物件的方式程式設計時,很多時候都需要用到this,大多數時候我們都不會去使用.bind(this),而是使用臨時變數self或者that來搬運this的取值,寫起來當然不是很爽,而且一不小心就會寫出BUG來。
正如MDN文件所說:
Until arrow functions, every new function defined its own this value based on how the function was called。This proved to be less than ideal with an object-oriented style of programming.
箭頭函式
箭頭函式的this取值,規則非常簡單,因為this在箭頭函式中,可以看做一個普通變數。
An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules.
箭頭函式沒有自己的this值,箭頭函式中所使用的this都是來自函式作用域鏈,它的取值遵循普通普通變數一樣的規則,在函式作用域鏈中一層一層往上找。
有了箭頭函式,我只要遵守下面的規則,this的問題就可以基本上不用管了:
-
對於需要使用object.method()方式呼叫的函式,使用普通函式定義,不要使用箭頭函式。物件方法中所使用的this值有確定的含義,指的就是object本身。
-
其他情況下,全部使用箭頭函式。
// 使用箭頭函式 var circle = { radius: 10, outerDiameter() { var innerDiameter = () => { console.log(2 * this.radius); }; innerDiameter(); } }; circle.outerDiameter(); // 列印20
對於內層函式innerDiameter,它本身並沒有this值,其使用的this來自作用域鏈,來自更高層函式的作用域。innerDiameter的外層函式outerDiameter是普通函式,它是有this值的,它的this值就是circle物件。因此,innerDiameter函式中所使用的this來自outerDiameter函式,其值為circle物件。
結論
JavaScript是Brendan Eich花了10天時間設計出來的,因此各種莫名其妙的特性,this也算是其中一個奇葩。好在這些年ECMAScript標準發展很快也很穩定,每年擼一個新的標準,多少可以彌補一下JS的先天不足。
箭頭函式對於this取值規則的簡化,其實也就是為了少給大家添亂,誰能記得住普通函式this取值的那麼多條條框框啊。。。
另外,MDN文件絕對是一個寶藏,大家可以多看看。
您可能感興趣的文章:
文章同步釋出: https://www.geek-share.com/detai