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

piz*_*r0b 26 javascript ecmascript-6 reactjs

令我困惑的是为什么当我定义一个反应组件类时,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>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在下面的示例中,我不需要使用.bind(this)或箭头函数,thisspeak函数中可以按预期使用

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();
Run Code Online (Sandbox Code Playgroud)

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

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

在React文档中,它说明如下

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

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

更新

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

更新2

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

更新3

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

事实上,这根本不是问题.this传递给onClick处理程序时的更改上下文,这就是JS的工作原理.

Fel*_*ing 13

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

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

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

<div>{this.renderElements()}</div>
//         call function ^^
Run Code Online (Sandbox Code Playgroud)

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


查看MDN以了解更多信息this.


Phi*_*yen 12

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

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
Run Code Online (Sandbox Code Playgroud)

查看此示例以了解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
Run Code Online (Sandbox Code Playgroud)

您可以查看此文章以获取更多信息.

  • `render`未在全局上下文中调用.React将调用`component.render()`,所以`this`将引用`component`.或者我误解了你对那句话的意思? (2认同)

Lyu*_*mir 8

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

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

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


React的文档是一个误导性的,当它说

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

他们的意思是

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

dog.speak() // this will be dog, because the function is called on dog
speak() // this will be window, and not dog, because the function is not bound
Run Code Online (Sandbox Code Playgroud)