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

时间:2017-12-06 17:01:29

标签: javascript reactjs this

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

看看这两段代码:

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

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

VS

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

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

我所知道的是:

  • 在两个版本中我们在goToStore方法中成功结束,因为this方法中的render()自动绑定(通过React)到组件实例
  • 第一个成功因为那个,
  • 第二个失败,因为es6中的类方法未绑定到组件实例,因此将方法中的this解析为undefined

据我所知,在第一版的理论中,会发生以下情况:

  1. 按钮点击处理程序是一个匿名箭头函数,因此每当我在其中引用this时,它都会从环境中获取this(在本例中来自{ {1}})
  2. 然后它调用render()方法,这是一个常规函数。
  3. 因为调用似乎符合隐式绑定的规则(goToStoreobject.function()将是上下文对象,在此类函数调用中它将用作object
  4. 所以,在this方法中,词法拾取(用作上下文对象)将正确解析为组件实例
  5. 我觉得3.和4.不是这里的情况,因为那样它将适用于2.案例:

    goToStore

    还有<button onClick={this.goToStore}>test</button> 上下文对象。

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

3 个答案:

答案 0 :(得分:4)

正如MDN文档陈述

  

箭头功能没有自己的功能;这个值   使用封闭执行上下文

所以你会想到

A extends any

是一个匿名函数,可以写成

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

现在这里的匿名函数 (e) => { return this.goToStore(e) } 引用了render函数的词汇上下文,而后者又引用了React Class实例。

现在

上下文通常取决于如何调用函数。当一个函数作为一个对象的方法被调用时,它被设置为调用该方法的对象:

this

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

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

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

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

所以这里因为函数被称为function foo() { alert(this); } foo() // window new foo() // foo ,所以它里面会引用React组件的上下文。

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

现在即使window有效,每当调用render时,都会创建一个新的函数实例。在你的情况下,它很容易避免,只需使用箭头函数语法创建函数goToStore。

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

查看文档以获取有关this

的更多详细信息

答案 1 :(得分:1)

在案例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,并且它将在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将绑定到运行构造函数的当前实例。

答案 2 :(得分:0)

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

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

This answer提供了一些更相关的信息和进一步阅读。