1. 程式人生 > >React:ES6:ES7中的6種this繫結方法

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