为什么我必须将.bind(this)用于React组件类中定义的方法,而不是常规ES6类中定义的方法

时间:2016-09-17 23:01:59

标签: javascript reactjs ecmascript-6

令我困惑的是为什么当我定义一个反应组件类时,this对象中包含的值在类中定义的方法(this在生命周期方法中可用)中是未定义的,除非我使用.bind(this)或使用箭头函数定义方法,例如在下面的代码中this.state将在renderElements函数中未定义,因为我没有用箭头函数定义它并且没有使用.bind(this)

class MyComponent extends React.Component {
    constructor() {
        super();
        this.state = { elements: 5 }
    }

    renderElements() {
        const output = [];

        // In the following this.state.elements will be undefined
        // because I have not used  .bind(this) on this method in the constructor
        // example: this.renderElements = this.renderElements.bind(this)
        for(let i = 0; i < this.state.elements; i ++){
            output.push(<div key={i} />);
        }

        return output;
    }

    // .this is defined inside of the lifecycle methods and 
    // therefore do not need call .bind(this) on the render method.
    render() {
        return (
            <div onClick={this.renderElements}></div>
        );
    }
}

然后在以下示例中,我不需要使用.bind(this)或箭头函数,this功能

中可以使用speak
class Animal { 
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(this.name + ' makes a noise.');
    }
    }

    class Dog extends Animal {
    speak() {
        console.log(this.name + ' barks.');
    }
}

var d = new Dog('Mitzie');
d.speak();

http://jsbin.com/cadoduxuye/edit?js,console

澄清一下,我的问题是两部分。一)为什么在第二个代码示例中我不需要将.bind(this)调用到speak函数,但我在renderElements函数的React组件中执行,以及为什么生命周期为什么?方法(render,componentDidMount等)已经可以访问类“this”对象,但renderElements没有。

在React文档中,它说明了以下内容

  

[React Component Class]方法遵循与常规ES6类相同的语义,这意味着它们不会自动将其绑定到实例。

但显然他们确实如此,正如我发布的第二个代码示例所示。

更新

前两个注释中的两个链接都显示了一个React类的工作示例,它不在类方法上使用.bind(this),它工作正常。但仍然在文档中明确表示您需要绑定您的方法,或使用箭头函数。在一个使用gulp和babel的项目中,我可以重现。这可能意味着浏览器有更新的东西?

更新2

我的初始代码示例在render函数中直接调用this.renderElements()。这将按预期工作而不绑定函数,或使用箭头函数定义它。当我将函数作为onClick处理程序时,会出现此问题。

更新3

  

当我将函数作为onClick处理程序时,会出现问题。

事实上,这根本不是问题。 this的上下文在传递给onClick处理程序时会发生变化,这就是JS的工作原理。

5 个答案:

答案 0 :(得分:13)

this的值主要取决于函数的调用方式。给定d.speak();this将引用d,因为该函数被称为“对象方法”。

但在<div>{this.renderElements}</div>中,您调用该函数。您正在将该函数传递给React,它将以某种方式调用它。当它被调用时,React不知道函数“属于”哪个对象,因此无法为this设置正确的值。 解决了这个问题

我实际上认为你真正想要的是

<div>{this.renderElements()}</div>
//         call function ^^

调用函数作为对象方法。然后你不必绑定它。

请查看MDN以了解有关this的更多信息。

答案 1 :(得分:13)

组件中的事件处理程序不会像其他方法一样自动绑定到组件实例(生命周期方法......)。

class MyComponent extends React.Component {
   render(){
      return (
         <div onClick={this.renderElements}>
             {this.renderElements()} <-- `this` is still in side the MyComponent context
         </div>
      )
   }
}
//under the hood

var instance = new MyComponent();
var element = instance.render();
//click on div
element.onClick() <-- `this` inside renderElements refers to the window object now

选中此示例以了解更多this

class Animal { 
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(this.name + ' makes a noise.');
    }  
}

class Dog extends Animal {
    run(){
       console.log(this.name + ' runs');
    }
    speak() {
        console.log(this.name + ' barks.');
        this.run(); <-- `this` is still in the Dog context
        return {onRun : this.run};
    }
}

var d = new Dog('Mitzie');
var myDog = d.speak();
myDog.onRun() <-- `this` is now in the global context which is the `window` object

您可以查看article以获取更多信息。

答案 2 :(得分:8)

ES6课程中的功能 - @Felix Kling很好地解释了这种情况。每次在对象上调用函数时,this都指向该对象。

React.Component中的生命周期方法 - 只要React实例化您的组件,如myComponent = new MyComponent(),它就知道调用生命周期方法的对象,即 myComponent 。因此,简单的调用myComponent.componentDidUpdate()可在this生命周期方法中提供componentDidUpdate。其他生命周期方法也是如此。

处理程序&amp;在React.Component中绑定 - this.stateundefined,因为this实际上是window - 请记录并查看。原因是 React在全局上下文中调用处理程序,除非您将处理程序绑定到另一个覆盖 window的上下文(请参阅@Phi Nguyen的答案)。 我认为他们这样做是为了让你更灵活,因为在复杂的应用程序中你的处理程序可能来自通过道具传递的另一个组件,然后你想有可能说:“嘿,React - {{1} }不是我的组件,但它是父组件。“

当React的文档说

时,其出价会产生误导
  

方法遵循与常规ES6类相同的语义,这意味着   它们不会自动将其绑定到实例。

他们的意思是

this

答案 3 :(得分:2)

1。 Arrow Functions:

  

箭头函数表达式与函数表达式相比具有更短的语法,词汇绑定此值does not bind its own this, arguments, super, or new.target)。箭头函数始终为anonymous。这些函数表达式最适合非方法函数,不能用作构造函数

Function.prototype.bind():

  

bind()方法创建一个新函数,在调用时,将其this关键字设置为提供的值,并在调用新函数时提供任何前面提供的给定参数序列。

2。Component Specs and Lifecycle

  

绝对清楚:大多数生命周期方法都没有绑定,但是使用点表示法调用实例(componentWillMountcomponentWillUnmountcomponentWillReceiveProps等等... ),除了componentDidMount,它绑定到实例,因为它被排入事务。

答案 4 :(得分:0)

只需将autoBind(this);代码放在构造函数中,永远不要担心方法指针。

npm install --save auto-bind-inheritance

const autoBind = require('auto-bind-inheritance');

class Animal {
  constructor(name) {
    autoBind(this);
    this.name = name;
  }

  printName() { console.log(this.name); }
  ...
}

let m = new Animal('dog');
let mpntr = m.printName;
m.printName() //> 'dog'
mpntr()       //> 'dog', because auto-bind, binds 'this' to the method.