05:为什么React的构造函数里总是要bind(this)?

2020-01-03  本文已影响0人  赛博剑仙_李玄器

在JavaScript中,以下两种写法是不等价的:

let obj = {
    tmp:'Yes!',
    testLog:function(){
        console.log(this.tmp);
    }
};

obj.testLog();      //方法一:此时为obj调用testLog函数,其中的this指向obj,所以结果为Yes

let tmpLog = obj.testLog;        //方法二
tmpLog();       //此时为window调用tmpLog函数,其中this指向window,但window没有定义tmp,所以结果为undefined

bind 方法确保了第二种写法与第一种写法相同。

看了好多博客都说是this丢失了指向,虽然可以这么理解。

但究其原因,我觉得只是因为在不同作用域里调用了同一个函数。而函数里面的this,由于指向不同外部环境从而产生了问题,this指向没丢,指的方向不同罢了。

bind() 方法创建一个新的函数,在 bind() 被调用时,该新函数的 this 指向 bind() 中的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

简单理解,当render里如果出现一个onClick={this.incrementAsync}
此时的步骤其实是

let incrementAsync = this.incrementAsync
onClick={ incrementAsync }

如果没有bind(this)将指向绑定到当前组件,此时的this.incrementAsync等于window.incrementAsync,而window里没有声明incrementAsync,所以会导致其值为undefined

import React, { Component } from 'react'

class Counter extends Component {
  constructor(props) {
    super(props);
    this.incrementAsync = this.incrementAsync.bind(this);   //bind()返回拥有指定this的原函数的拷贝,然后初始化赋值给左端
  }

  incrementAsync() {
    setTimeout(this.props.onIncrement, 1000)
  }

  render() {
    return (
      <p>
        <button onClick={this.incrementAsync}>  //如果没有constructor里的bind,该this指向window
          Increment async
        </button>
      </p>
    )
  }
}

如果你不喜欢在构造函数里用bind,那么你可以在回调中使用一个箭头函数:

import React, { Component } from 'react'

  incrementAsync() {
    setTimeout(this.props.onIncrement, 1000)
  }

  render() {
    return (
      <p>
        <button onClick={ (e) => this.incrementAsync(e) }>  
          Increment async
        </button>
      </p>
    )
  }
}

这个语法的问题是,每次 LoggingButton 渲染时都创建一个不同的回调。在多数情况下,没什么问题。然而,如果这个回调被作为 prop(属性) 传递给下级组件,这些组件可能需要额外的重复渲染。我们通常建议在构造函数中进行绑定,以避免这类性能问题。

上一篇 下一篇

猜你喜欢

热点阅读