React創建組件的方法;Class組件爲什麼要綁定this?

在寫這個問題之前,我們先要學習2個知識點;

  • 1 ES6 Class的基本語法
  • 2 React創建組件的方法;

ES6 Class的基本語法

在ES6 引入了 Class(類)這個概念,作爲對象的模板。通過class關鍵字,可以定義類。
我這裏不做過多的介紹,具體請參考ECMAScript 6 入門(阮一峯)

(1) ES6 Class 編寫對象

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

以上的代碼等價於:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

在這裏會有2個概念: 類的實例和對象的屬性

接下來我們用一段代碼解釋這兩個東西;

//定義類
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
// 實例化Point Class
var point = new Point(2, 3);
console.log(point.toString()) // (2, 3)
console.log(point.hasOwnProperty('x')) // true
console.log(point.hasOwnProperty('y')) // true

// 判斷對象的實例上是否有 toStirng 屬性
console.log(point.hasOwnProperty('toString')) // false

// 判斷對象的屬性上是否存在 toString 屬性
console.log(point.__proto__.hasOwnProperty('toString')) // true

基於上面的代碼我們可以得知:

  • 實例的屬性除非顯式定義在其本身(即定義在this對象上),否則都是定義在原型上(即定義在class上)
  • 上面代碼中,x和y都是實例對象point自身的屬性(因爲定義在this變量上),所以hasOwnProperty方法返回true,而toString是原型對象的屬性(因爲定義在Point類上),
  • 所以hasOwnProperty方法返回false。這些都與 ES5 的行爲保持一致。

(2) this的指向

類的方法內部如果含有this,它默認指向類的實例;但是,必須非常小心,一旦單獨使用該方法,很可能報錯。

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const logger = new Logger();
// ES6 解構
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined

上面代碼中,printName方法中的this,默認指向Logger類的實例。但是,如果將這個方法提取出來單獨使用,this會指向該方法運行時所在的環境(由於 class 內部是嚴格模式,所以 this 實際指向的是undefined),從而導致找不到print方法而報錯。

因此在如果這個方法被單獨提出來使用,我們的通常的做法就是在構造方法中綁定this,這樣就不會找不到print方法了。

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}

另一種方法是使用箭頭函數

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}

const myObj = new Obj();
myObj.getThis() === myObj // true

箭頭函數內部的this總是指向定義時所在的對象,
它的定義生效的時候,是在構造函數執行的時候。
這時,箭頭函數所在的運行環境,肯定是實例對象,所以this會總是指向實例對象

第三種是使用還有一種解決方法是使用Proxy,獲取方法的時候,自動綁定this。具體請看:ES6 Class Proxy

那麼以上就是this在ES6中Class的基本介紹;詳細的學習請參考ECMAScript 6 入門(阮一峯),那麼接下來我們就來看看React中如何使用this;首先會從組件的創建開始來說;

React創建組件的寫法

1.函數組件(無狀態組件)

定義組件最簡單的方式就是編寫 JavaScript 函數:
(1) 語法

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

(2) 特點

  • 它是爲了創建純展示組件,這種組件只負責根據傳入的props來展示,不涉及到state狀態的操作。
  • 組件不能訪問this對象
    無狀態組件由於沒有實例化過程,所以無法訪問組件this中的對象,例如:this.ref、this.state等均不能訪問。若想訪問就不能使用這種形式來創建組件
  • 它不能訪問生命週期方法

2 es5方式React.createClass組件(有狀態組件)

React.createClass是react剛開始推薦的創建組件的方式,這是ES5的原生的JavaScript來實現的React組件
(1)語法

var myCreate = React.createClass({
    defaultProps: { 
        //code
    },
    getInitialState: function() {
        return {
            //code
        };
    },
    render: function() {
        return (
            <div>
                //code
            </div>
        );
    }
});

(2) 特點

與無狀態組件相比,React.createClass和後面要描述的React.Component都是創建有狀態的組件,這些組件是要被實例化的,並且可以訪問組件的生命週期方法。但是隨着React的發展,React.createClass形式自身的問題暴露出來:

  • React.createClass會自綁定函數方法(不像React.Component只綁定需要關心的函數)導致不必要的性能開銷,增加代碼過時的可能性。
  • React.createClass的mixins不夠自然、直觀;React.Component形式非常適合高階組件(Higher Order Components–HOC),它以更直觀的形式展示了比mixins更強大的功能,並且HOC是純淨的JavaScript,不用擔心他們會被廢棄。HOC可以參考無狀態組件(Stateless Component) 與高階組件

3.Class組件(有狀態組件)

React.Component是以ES6的形式來創建react的組件的,是React目前極爲推薦的創建有狀態組件的方式,最終也會取代React.createClass形式;
它相比較於 React.createClass可以更好實現代碼複用。將上面React.createClass的形式改爲React.Component形式如下:

class InputControlES6 extends React.Component {
    constructor(props) {
        super(props);

        // 設置 initial state
        this.state = {
            text: props.initialValue || 'placeholder'
        };

        // ES6 類中函數必須手動綁定
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({
            text: event.target.value
        });
    }

    render() {
        return (
            <div>
                Type something:
                <input onChange={this.handleChange}
               value={this.state.text} />
            </div>
        );
    }
}
InputControlES6.propTypes = {
    initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
    initialValue: ''
};

(2)優點:

  • 成員函數不會自動綁定this,需要開發者手動綁定,否則this不能獲取當前組件實例對象。
  • 狀態state是在constructor中像初始化。
  • props屬性類型和組件默認屬性作爲組件類的屬性,不是組件實例的屬性,所以使用類的靜態屬性
  • 能很好的實現代碼複用
  • 能很好的管理組件的生命週期

React.createClass與React.Component區別

(1) 函數的this綁定

React.createClass創建的組件,其每一個成員函數的this都有React自動綁定,任何時候使用,直接使用this.method即可,函數中的this會被正確設置。

const Contacts = React.createClass({  
  handleClick() {
    console.log(this); // React Component instance
  },
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
});

而用React.Component創建的組件,其成員函數不會自動綁定this,需要開發者手動綁定,否則this不能獲取當前組件實例對象。

class Contacts extends React.Component {  
  constructor(props) {
    super(props);
  }
  handleClick() {
    console.log(this); // null
  }
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }

在這裏就可以看到;因爲我們使用了ES6的Class方式來創建組件,那麼如何在內部去訪問屬性方法;就需要顯式的在需要綁定的地方綁定this;

當然,React.Component有三種手動綁定方法:

  • 可以在構造函數中完成綁定,
  • 在調用時使用method.bind(this)來完成綁定,
  • 還可以使用箭頭函數來綁定

那麼我們以上面的handleClick函數爲例子;

在構造函數中綁定;

constructor(props) {
       super(props);
       this.handleClick = this.handleClick.bind(this); //構造函數中綁定
  }

在調用時候的進行綁定

 <div onClick={this.handleClick.bind(this)}></div> //使用bind來綁定

使用箭頭函數來綁定

    <div onClick={()=>this.handleClick()}></div> //使用arrow function來綁定

基本上就這些吧;以後碰上了再寫;

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