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
。
譯文參考于 6 Ways to Bind JavaScript‘s this Keyword in React, ES6 & ES7
來源 http://www.ido321.com/1670.html