ES6的class本質和react中需要使用bind(this)的原因

ES6的class

我們知道ES6新出一個規範是使用class關鍵字來定義一個類,這在以前是沒有的
在以前,需要在javascript裏實現面向對象,就需要使用prototype

什麼是面向對象?

面向對象有如下幾個基本特徵,通常認爲,只要實現了這幾個特性,就認爲是實現了面向對象:
1.封裝
2.繼承
3.多態
這裏不對三個基本特徵的做詳細展開。只需要知道,javascript實現繼承和多態都需要用到prototype
而ES6的class,本質上還是ES5的prototype的語法糖

什麼是語法糖?

語法糖就是提供了一種全新的方式書寫代碼,但是其實現原理與之前的寫法相同。
語法糖可以說是廣泛存在於各種計算機代碼中,包括C語言中的a[i]其實就是*a+i的語法糖。而今天對於我們來說,a[i]其實已經很普遍和常用了,所以也沒有人提這是語法糖這回事了。因爲終極來說,所有語言都是彙編語言的語法糖:)

class foo{
    constructor(){
    }
    a(){}
    b(){}
}

// 等價於
function foo(){};
foo.prototype = {
    constructor(){},
    a(){},
    b(){},
}

ES6的class跟ES5的定義方式用幾個不同

1.沒有變量提升
2.this指向不同

先來看1:

test(); // 輸出'test'
function test(){
    console.log('test');
}

我們知道即便在定義test函數之前執行test(),也是會得到結果的。這是因爲解析javascript代碼的時候會把所有的function test(){}這類代碼(即正常定義函數)都提升到最上方去定義。

但是這麼執行不行:

let a = new test(); // 報錯
class test(){} 

再看2:

class Animal {
    printName () {
      this.print('Hello animal');
    }
    print(name) {
      console.log(name);
    }
}
const animal = new Animal();
animal.printName(); // 輸出'Hello animal'
const { printName } = animal;
printName(); // 報錯: Cannot read property 'print' of undefined

如果執行了bind

class Animal {
    constructor(){
        this.printName = this.printName.bind(this);
    }
    printName () {
      this.print('Hello animal');
    }
    print(name) {
      console.log(name);
    }
}
const animal = new Animal();
animal.printName(); // 輸出'Hello animal'
const { printName } = animal;
printName(); // 輸出'Hello animal'

發生了什麼?

animal中的printName函數的this原本指向的是執行環境,如果不執行bind,那麼printName函數的this指向window。
在執行new Animal()的時候,如果執行了bind,那麼從animal中獲取的printName函數,其this對象已經被綁定到了constructor的this,即animal上。
以下是this的指向
clipboard.png

那麼我們爲什麼需要在react裏bind(this)呢?

簡單來說,就是react在調用render方法的時候,會先把render裏的方法賦值給一個變量(比如變量foo),然後再執行foo()。
具體來說,以典型的綁定點擊事件爲例

<div onClick={this.clickHandler}></div>

react構建虛擬DOM的時候,會把this.clickHandler先賦值給一個變量。我們假設是變量clickFunc = this.clickHandler;
然後,把虛擬DOM渲染成真實DOM的時候,會把onClick屬性值替換成onclick,並給onclick賦值clickFunc

在複雜的情況中,可能存在多次傳遞,如果不進行bind,那麼this的指向是一定會丟失的。

爲什麼react不自己集成bind到生命週期裏?

1是,沒有特別合適集成bind的地方
2是,並不是所有的函數都需要bind
3是,隨意集成bind可能導致效率低下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章