React:ES6:ES7中的6種this綁定方法

hubuke 8年前發布 | 50K 次閱讀 ES6 ES7 JavaScript開發 JavaScript 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

 本文由用戶 hubuke 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!