React元件方法的兩種定義方式

cherryPick發表於2018-12-03

在React元件中,通常可通過兩種方式來定義元件的方法:普通函式、箭頭函式;作為方法存在,那麼兩者到底有什麼區別呢?

方法掛載點

es6,有一部分是對過去版本的js進行包裝。在使用es6語法的React元件中,要剖析普通函式和箭頭函式定義方法有什麼區別,可通過babel轉換,轉換成我們熟悉的js語法,看看二者的本來面目。

舉個例子:

    class Hello {

        greet(){ },

        say = () => { },
    }
複製程式碼

babel轉換後,突出重點,大致如下:

function Hello(){
    this.say = () => { }
}
function.prototype.greet = function() { }
複製程式碼

通過轉義後的程式碼,可看到區別:

  • 普通函式被定義為類的原型方法

    通過function建立的原型方法。

  • 箭頭函式被定義為類的例項方法

    通過箭頭方法建立的例項方法。

原型方法和例項方法?

  • 屬性查詢 在查詢例項的屬性和方法時,首先會在例項的屬性、方法中查詢,當沒有找到時,在循著例項的原型鏈進行查詢,直到查詢到對應的屬性和和方法,或者最後什麼都查不到。這就和效能相關了,查詢屬性、方法的時間有過不同。
  • 呼叫 例項方法只能通過例項呼叫,而原型方法還能通過函式呼叫,比如:A.prototype.click()。

this指向

在es6之前的js語法中,this的指向是一個比較多場景的問題在Js中,在此忽略。我們今天只講在React元件中這兩種方法的this指向。從上面得出,兩個方法的在類中的的掛載點是不同的,然而,這並不是導致他們作為方法,表現差異有所不同的原因,更多的是他們的的建立方式導致this有所區別。

箭頭函式定義的方法

箭頭函式定義方法時,this指向的是函式定義時的作用域。看例子:

 function A(){
    this.num = 2;
    this.handleClick = () => { console.log(this.num) };
}

const B = new A();

// 第一種情況
B.handleClick(); //2
// 第二種情況
const fn = b.handleClick;
fn(); // 2
複製程式碼

普通函式定義的方法

而普通函式的this,卻是根據呼叫它時的物件有所關係。

function A(){
   this.num = 2
   this.handleClick = function(){console.log(this.num) }
}

const B = new A();

//以下都為嚴格模式下

B.handleHover(); // 2

const fn = B.handleHover;

fn(); // 報錯,因為在嚴格模式下this為undefined
複製程式碼

無論是原型方法或者是例項方法,導致在呼叫時,this不同,原因在於他們的建立方式,當使用箭頭函式建立,那麼this將指向函式定義時的作用域;而當使用普通的function建立時,this的指向和函式的呼叫環境有關,(關於普通函式this的指向有所不瞭解,可通過《你不知道的javascript》熟悉)

所以,無論如何,箭頭函式的this都不會發生改變,this指向的是元件;而普通函式的this隨著呼叫場景的變化有所變化。

方法使用

如果我們在需要將元件的一個方法繫結給一個子元素,為了保證函式中的this指向的是這個React元件(當函式需要藉助元件的一些狀態和屬性時)。

class Hello extends React.PureComponent {
    greet() {console.log(this)}
    say = () => { console.log(this)}
    
    render() {
        return (
            <div>
                // 普通函式
                <div onClIck={this.greet.bind(this)}>普通函式</div>
                <div onClick={(e) => this.greet(e)}></div>

                // 箭頭函式
                <div onClick={this.say}></div>
            </div>
        )
    }
}

複製程式碼

普通函式的傳遞方法視覺上就覺得有點繁瑣,除此之外,更重要的是效能方面。

React的生命週期中,shouldUpdateComponent,當shouldUpdateComponent返回false時,元件將不會重新render,子元件也可避免重新render。在 React 15.3.0 ,Reac可通過React.Purecomponent定義元件,當shouldUpdateComponent進行shallow compare時,避免一些不必要的render。而通過普通函式定義的方法,通過bind繫結後,每次父元件發生render時,方法就得重新bind(this),對於子元件而言,它的props、state發生了改變,則必須發生重新渲染。

如果我們定義的方法不用繫結給子元素,而是被元件自己內部的函式呼叫,箭頭函式和普通函式都可以用來定義方法。

class DownloadGameBtn extends React.PureComponent {
    constructor(props) {
        super(props);
        this.initInfo();
        this.initSize();
    }
    initInfo() {
       console.log(this); // 指向DownloadGameBtn元件 
    }
    initSize = () => {
        console.log(this) // 指向DownloadGameBtn元件
    }
}
複製程式碼

參考

ES6 Class Methods 定義方式的差異

函式作為React元件的方法時, 箭頭函式和普通函式的區別是什麼?

最後

在工作中遇到的問題,通過各種資料查詢,進行總結和歸納,感謝各位前輩的分享。這是第一次在一個公共社群發表自己的總結文章,寫的有點蠢蠢的,希望這些可以幫到和我一樣遇到類似困惑的朋友。然後,大家輕點噴。

相關文章