React:ES6:ES7中的6種this繫結方法
對於大多數開發者來說,JavaScript 的 this 關鍵字會造成諸多困擾。由於 JavaScript 不具備如 Java 等語言的嚴格類模型,因而除非是在處理回撥,否則程式碼中的this
指向並不清晰。
一般來說,對於部分執行中的程式碼(非回撥)會通過 new
關鍵字和 Function.prototype
提供的一些方法,如call
/apply
等來繫結函式的上下文。
問題
在每個 class
中,React 使用 this
指向元件本身,這會給開發者造成一些困擾。如在 React 元件中,可能會經常看到類似如下的程式碼:
this.setState({ loading: true });
fetch('/' ).then(function loaded() {
this.setState({ loading: false });
});
上述程式碼會造成 TypeError
,因為 this.setState
不是一個函式。丟擲TypeError
的原因是當 promise 的回撥被呼叫時,內部的上下文已經改變了,this
指向了錯誤的物件。
那麼,怎麼正確繫結程式碼中的 this
呢?
選擇
本文提供的 6 種方式中,有一些是比較老的技術,另一些是針對 React 的,還有一些可能瀏覽器也不支援,但還是值得探討一下。
1、this 別名
這種方式就是在 React 元件的作用域頂端建立一個指向 this
var component = this;
component.setState({ loading: true });
fetch('/').then(function loaded() {
component.setState({ loading: false });
});
這種方式方便,易於理解,並能保證 this
會指向正確的上下文。
2、.bind(this)
這種方式是在函式執行時將 this
注入到回撥中,使回撥中的 this
能指向正確的上下文:
this.setState({ loading: true });
fetch('/').then(function loaded() {
this.setState({ loading: false });
}.bind(this));
在 JavaScript 中,所有函式都有
bind 方法,其允許你為 this
指定特定值。一旦函式被繫結,上下文就不能被覆蓋,也就意味著 this
會指向正確的上下文。
3、React Component Methods
當使用 React.createClass
來定義元件時,React 允許你隨意在此元件的類中定義方法,而在方法中呼叫的
this
會自動繫結到元件自身:
React.createClass({
componentWillMount: function() {
this.setState({ loading: true });
fetch('/').then(this.loaded);
},
loaded: function loaded() {
this.setState({ loading: false });
}
});
對於不是非常複雜的元件來說,這是一種非常不錯的解決 this
指向問題的方式。而事實上呢,如果在元件的方法中使用 .bind(this)
,React 會丟擲一個警告:
bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call.
但對於 ES2015 的類 來說,自動繫結並不適用。
4、箭頭函式
ES2015 規範引入了箭頭函式,使函式的定義更加簡潔。箭頭函式會隱式返回一個值,但更重要的是,它是在一個封閉的作用域中使用this
:
this.setState({ loading: true });
fetch('/').then(() => {
this.setState({ loading: false });
});
不管巢狀多少層,箭頭函式中的 this
總能指向正確的上下文,因為函式體內的 this
指向的物件,就是定義時所在的物件,而不是使用時所在的物件。但缺點就是,由於箭頭函式不能命名,因而在除錯時,堆疊資訊給的標籤是anonymous function
。
如果你用 Babel 將 ES6 的程式碼轉換成 ES5 的程式碼,就會發現兩個有趣的現象:
- 在某些情況下,編譯器能判斷函式名是否被賦值給了某個變數
- 編譯器使用 別名 來維護上下文
const loaded = () => {
this.setState({ loading: false });
};
// will be compiled to
var _this = this;
var loaded = function loaded() {
_this.setState({ loading: false });
};
5、ES7 的繫結語法
在 ES7 中,有一個關於
bind 語法 的提議,提議將 ::
作為一個新的繫結操作符,該操作符會將左值和右值(一個函式)進行繫結。
以 map
的實現為例:
function map(f) {
var mapped = new Array(this.length);
for(var i = 0; i < this.length; i++) {
mapped[i] = f(this[i], i);
}
return mapped;
}
與 lodash 不同,我們不需要傳遞資料給 map
作為引數:
[1, 2, 3]::map(x => x * 2)
// [2, 4, 6]
對下面的程式碼熟悉嗎?
[].map.call(someNodeList, myFn);
// or
Array.from(someNodeList).map(myFn);
ES7 的繫結語法允許你像使用箭頭函式一樣使用 map
:
someNodeList::map(myFn);
在 React 中也是可以使用的:
this.setState({ loading: true });
fetch('/').then(this::() => {
this.setState({ loading: false });
});
6、方法傳參指定
一些函式允許為 this
傳遞一個明確的值,保證其指向正確的上下文,例如 map
函式則將
this
作為最後一個引數:
items.map(function(x) {
return <a onClick={this.clicked}>x</a>;
}, this);
雖然程式碼能執行,但這不是函式的一致實現。大部分函式並不接受 this
引數,所以最好還是採用上文中的其它方式來繫結 this
。
來源 http://www.ido321.com/1670.html