【问题标题】:How does React call the render function of an ES6 class in a such a way that `this` does not refer to the class itself?React 如何调用 ES6 类的渲染函数,使得 `this` 不引用类本身?
【发布时间】:2016-04-09 03:12:23
【问题描述】:

例如,给定具有函数 increaseQty 的类

increaseQty() {
  this.qty++
}

和电话

render() {
  return (
    <div>
      <button onClick={this.increaseQty}>Increase</button>
    </div>
  )
}

除非我在构造函数中写一行将构造函数中this 的上下文绑定到函数,否则this.qty 将是未定义的

constructor(props) {
  super(props)
  this.qty = 0
  this.increaseQty = this.increaseQty.bind(this) // <---- like so
}

但是,如果您只是正常使用它,那么在普通的 es6 类中情况并非如此:

https://jsfiddle.net/omrf0t20/2/

class Test {
  constructor() {
    this.qty = 0
  }

  increaseQty() {
    console.log(++this.qty)
  }

  doStuff() {
    this.increaseQty()
  }
}

const t = new Test()
t.doStuff() // prints 1

React 的什么方面使得 render 在没有 this 上下文的情况下被调用?

【问题讨论】:

  • “这还不是标准” 箭头函数已包含在去年标准化的 ES6 规范中。他们哪儿也不去。
  • @JeremyBanks 你是对的,但这与此无关。我目前正在谈论的是第一阶段提案github.com/jeffmo/es-class-fields-and-static-properties
  • 是的,我怀疑是这样的。这就是为什么我想看一个完整的例子。它不一定是only React。但总的来说,使用class 语法创建的方法的行为类似于通过 FunctionName.prototype.methodName =... 创建的老式语法,这意味着除非您使用 .bind() ,否则有几种方法可以更改 this 的内容将在调用时。
  • (我并不真正了解 React,但我假设)您的 React 渲染调用失败的原因与在任何回调情况下传递对该方法的引用都会失败的原因相同。例如。 setTimeout(this.increaseQty, 10) 以同样的方式失败。在 JS 中,“方法”并不真正属于对象,“方法”只是一个引用函数的对象属性,在渲染代码中,您传递的是对函数的引用,而不是对象。
  • 这种现象与 React 无关,这只是 this 在 JavaScript 中的工作方式。看看MDN page about this

标签: javascript class reactjs ecmascript-6 this


【解决方案1】:

这里的区别在于,在您使用 React 的示例中,您将 increaseQty 作为回调传递给另一个组件,但在第二个中,您是在当前上下文中调用它。

您可以在简化示例中看到这里的区别

class Test {
  constructor() {
    this.qty = 0
  }

  increaseQty() {
    console.log(++this.qty)
  }

  doStuff() {
    this.increaseQty(); // no need to bind
  }

  listenClicks() {
    // you should use bind to preserve context
    document.body.addEventListener('click', this.increaseQty.bind(this)); 
  }
}

React 指南还建议您在构造函数中绑定方法,以使代码更优化,绑定一次并始终使用相同的函数,而不是为每个 render() 调用创建一个新的绑定版本。

【讨论】:

  • 我不知道 addEventListener 改变了this 的上下文是怎么想的,谢谢。
【解决方案2】:

这并不是一个真正的 ES6 特定问题(除了我们引用一个类和构造函数的事实)。您在函数中所做的只是增加一个值。如果该值没有被初始化(即使在 ES5 中),那么它会抛出一个错误。 undefined 不能加 1。

在 ES5(实际上是 ES6)中,这将是一个问题:

var myObj = {
    addOne: function() {
        this.qty++;
    }
}

myObj.addOne(); // Error! this.qty is undefined

而这会解决它:

var myObj = {
    qty: 0,
    addOne: function() {
        this.qty++;
    }
}

myObj.addOne(); // Good to go

你们班的情况也一样。您不能将尚未声明并初始化为数值的变量递增。

在一个更简单的例子中:

var x;

x++;

会抛出一个错误,而这个:

var x = 0;

x++;

很好。

【讨论】:

  • 请注意,您不必执行任何类似于 myObj.addOne = myObj.addOne.bind(myObj) 的操作。你并没有真正解决我的问题。即使在构造函数中定义了this.qty,如果你不将this绑定到函数increaseQty,它也会有一个未定义的this
猜你喜欢
  • 2019-09-11
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 2021-01-07
  • 1970-01-01
  • 1970-01-01
  • 2017-03-10
  • 2012-04-08
相关资源
最近更新 更多