在React ClassComponent中繫結方法的正確姿勢

tsker發表於2018-11-24

使用react的小夥伴日漸增多,react簡單的api讓人很是快速上手,但是有時候,稍不注意,做出的東西也許不是看上去那麼美好,其中就有一個,那就是在React ClassComponent中,究竟使用哪種繫結姿勢好一點。

@Monster000 的文章 React.js繫結this的5種方法, 詳細的介紹了react的常用繫結姿勢,感謝 @Monster000 的付出。關於文章中討論的全部方法,前三種我們們拋去不談,只聊聊第四種bind繫結與第五種箭頭繫結

先從class說起

class ArrowComponent{
    method = () => {
        // more code
    }
}
複製程式碼

這裡是一個class Component, 大部分人初看,沒問題啊,標準寫法。
標準寫法?標準嗎?
先來這看看 -> 傳送門: Class 的基本語法

看完後發現,全篇討論都是建立在class上定義方法,只有一個關於屬性的,還是私有屬性,而且現階段不支援,寫法是這樣的

class Component{
    #name = ``
}
複製程式碼
在React ClassComponent中繫結方法的正確姿勢

那到底支援不支援正常的表示式屬性呢,試驗一下

在React ClassComponent中繫結方法的正確姿勢
在React ClassComponent中繫結方法的正確姿勢

嘖嘖,可見,上面說的標準寫法,現在不是標準的同志們,它是不支援的。

現在回到現實,為什麼我們專案中這樣寫不會報錯呢?
那是因為現在能用上這麼新的語法的,沒有人在幹擼code, 都從babel或者typescript過了一下,那為什麼他們不報錯呢,不多說,直接實踐:

在React ClassComponent中繫結方法的正確姿勢

typescript 演示地址

在React ClassComponent中繫結方法的正確姿勢

babel 演示地址

熟悉js原型鏈的同學一看就明白了,這不是我們想要的,我們想要的是這樣的:

在React ClassComponent中繫結方法的正確姿勢

到此,class的一些問題告一段落現在我們,在回過頭看bind繫結箭頭繫結

箭頭繫結

通過上面得知,class中的箭頭方法暫時不支援,但為了讓我們寫頭爽,各種編譯器默許了這種存在,只不過在編譯結果上做了一點改變,如果只是一般的屬性定義,也無所謂,但是用這種寫法去bind this,嘖嘖,分析一下

function ComponentA(){
    this.method = () => {
        // more code
    }
}
function ComponentB(){
    constructor(){
        this.method = thid.method.bind(this)
    }
}
// 提醒一下,prototype.method不要用 () => ,不然會將this繫結到當前context, 且,無法再次改變this指向,.bind也不行
ComponentB.prototype.method = function(){}
複製程式碼

兩種寫法,一眼就能看懂

A中,每次生成A的例項時, method都會重新定義一次,是每一次,EveryTime,且只用A有效
(假如說有一1w個這樣的元件?每一個react元件都是X的例項,一個頁面有多少元件?)
B中,method只會定義一次,所有例項可直接使用。只有在react元件中需要繫結
(只所以這樣,是facebook官方怕有的人對這裡this產生誤解,很早之前其實是不用bind這一步的)

有的同學會說,bind不是也會生成一個新的func嗎?

再說bind

函式是有大小的,同學們

    function a(){
        // 1 line code
    }
    function b(){
        // 10w line code
    }
    
    const c = a.bind(null)
    const d = b.bind(null)
複製程式碼

這裡,a 跟 b 是同樣的大小嗎? 不是。
但 c 跟 d 是一樣大小的。
不信,上 bind:

Function.prototype.bind = function(context){
  var args = Array.prototype.slice(arguments, 1),
  F = function(){},
  self = this,
  bound = function(){
      var innerArgs = Array.prototype.slice.call(arguments);
      var finalArgs = args.concat(innerArgs);
      return self.apply((this instanceof F ? this : context), finalArgs);
  };

  F.prototype = self.prototype;
  bound.prototype = new F();
  retrun bound;
};
複製程式碼

不管你函式有多大,bind只走自己的邏輯,在執行的時候呼叫一下繫結的那個函式而已。

總結

全手寫,累了,簡單結論,React ClassComponen中,錯,不只是React ClassComponen, 而是在現階段 Es Class中,不要使用箭頭定義方法, 因為這種寫法跟 js原型鏈一點關係沒有,完全放棄了例項共享原型鏈的優勢。

玩的愉快.

相關文章