带有箭头函数的事件处理程序如何实现上下文绑定

IT技术 javascript reactjs this
2021-03-15 05:32:57

我了解this绑定的一般理论(重要的函数调用站点、隐式、显式绑定等...)以及在 React 中解决此绑定问题的方法,因此它始终指向我想要this成为的对象(绑定在构造函数、箭头函数等),但我正在努力获得内部机制。

看一下这两段代码:

class demo extends React.component {
  goToStore(event) {
    console.log(this)
  }

  render() {
    <button onClick={(e) => this.goToStore(e)}>test</button>
  }
}

对比

class demo extends React.component {
  goToStore(event) {
    console.log(this)
  }

  render() {
    <button onClick={this.goToStore}>test</button>
  }
}

我所知道的是:

  • 在两个版本中,我们最终都成功地使用了 goToStore 方法,因为thisrender()方法内部自动绑定(通过 React)到组件实例
  • 因为第一个成功,
  • 第二个失败,因为es6中的类方法没有绑定到组件实例,因此this在方法中解析undefined

据我了解,理论上在第一个版本中,会发生以下情况:

  1. 按钮单击处理程序是一个匿名箭头函数,因此每当我在其中引用this时,它都会this从环境中获取(在本例中来自render()
  2. 然后它调用该goToStore方法,即一个常规函数。
  3. 因为调用似乎符合隐式绑定的规则 ( object.function())object将是上下文对象,并且在这样的函数调用中它将被用作this
  4. 因此,在该goToStore方法中,词法拾取 this(用作上下文对象)将正确解析为组件实例

我觉得 3. 和 4. 不是这里的情况,因为它会适用于 2. 情况:

<button onClick={this.goToStore}>test</button>

也与this上下文对象。

在这个特殊情况下,一步一步到底发生了什么?

3个回答

正如 MDN 文档所述

箭头函数没有自己的 this;使用封闭执行上下文的 this 值

所以你会想到

onClick={(e) => this.goToStore(e)}

作为一个匿名函数,可以写成

    (e) => { 
         return this.goToStore(e) 
    }

现在这里的匿名函数this指的是渲染函数的词法上下文,而渲染函数又指的是 React Class 实例。

现在

上下文通常由函数的调用方式决定。当函数作为对象的方法被调用时, this 被设置为调用该方法的对象:

var obj = {
    foo: function() {
        return this;   
    }
};

obj.foo() === obj; // true

当使用 new 运算符调用函数来创建对象的实例时,同样的原则也适用。当以这种方式调用时,函数范围内的this的值将被设置为新创建的实例:

function foo() {
    alert(this);
}

foo() // window
new foo() // foo

当作为未绑定函数调用时,这将默认为浏览器中的全局上下文或窗口对象。

所以这里因为函数this.goToStore() 在内部是这样调用的,所以它会引用 React 组件的上下文。

但是,当您编写时onClick={this.goToStore},不会执行该函数,而是将它的引用分配给 onClick 函数,然后该函数随后调用它,导致this在函数内部未定义,因为该函数在window对象的上下文中运行

现在即使onClick={(e) => this.goToStore(e)}有效,每当调用 render 时,都会创建一个新的函数实例。在您的情况下,它很容易避免,只需使用箭头函数语法创建函数 goToStore 即可。

goToStore = (e) => {

}

检查文档以获取更多详细信息 this

现在我明白了,所以稍后当点击发生时,通过一个简单的函数调用来调用函数引用,比如onClick(),导致默认绑定规则,即 window。谢谢!
2021-04-24 05:32:57

在情况 1 中,正如您所说,上下文是从环境中获取的。对于情况 2,您必须记住,ES6 中的类语法只是 JavaScript 依赖于实现 OOP 的更繁琐的原型语法的语法糖。

基本上,在第二个示例中,您所做的就是这样:

function demo() {
   // this is the constructor
}
demo.prototype.goToStore = function() {
  // handler
};
demo.prototype.render = function() {
  return React.createElement('button', onClick: this.goToStore);
}

如您所见,该onClick属性只是接收对该函数的引用。无论何时调用该函数,this都不会绑定no ,并且它将在window对象的上下文中运行

在较旧的库中,在现代转译器出现之前,我们曾经在整个地方做这样的事情:

function demo() {
   // this is the constructor     
   this.goToStore = this.goToStore.bind(this);
   // same for every other handler
}
demo.prototype.goToStore = function() {
   // handling code.
};
demo.prototype.render = function() {
  // this goToStore reference has the proper this binded.
  return React.createElement('button', onClick: this.goToStore);
}

如今,我提出的最后一个示例由所有现代转译器自动处理。当您在任何类中使用粗箭头方法语法时,Babel 基本上会在构造函数中进行自动绑定:

class demo extends Anything {
  constructor() {
  }
  bindedMethod = () => {
    // my context will always be the instance!
  }
}

有细微的差别,所有的转译器都会将 的定义移动bindedMethod到构造函数,在那里this将绑定到运行构造函数的当前实例。

谢谢,现在明白了。您的回答与 Shubham 的回答一样好,如果允许接受两个答案,我也会接受。
2021-05-02 05:32:57

当渲染代码执行时,this引用组件类,所以它能够引用正确的函数。这意味着element.onClick指向goToStore方法。

当在浏览器中调用该函数时,它是从 html 元素(即element.onClick()的上下文中调用的因此,除非该goToStore方法绑定到组件类的上下文,否则它将this从元素的上下文中继承

这个答案提供了一些更相关的信息和进一步阅读。