类和箭头函数以及THIS

7
在学习JS和React时,我遇到了教程中的混淆差异。
下面我将以示例形式列出问题。我理解正常函数和this上下文中的绑定,但箭头函数以及我看到的它们的使用/声明方式令人困惑。
请不要只是参考重复内容,因为我找到的教程答案相互矛盾,使我感到困惑,所以要寻求简单理解的真相来源。与以下问题和示例相关的答案将有助于更好地理解。
1-我见过一些教程的示例,说箭头函数的“this”值将继承自全局/窗口范围,但我也见过一些教程说它将从类上下文继承“this”-哪个是正确的?如果您能解释一下就太好了。

class MyClass {
    value = 'Hello World!'
    clickHandler = () => { console.log(this.value) };
}

2 - 我有两个问题:

i - 为什么语法是clickHandler = () =>而不是clickHandler () =>?

我问这个问题是因为我读过类方法可以用'functionName () {}'来定义,那么为什么箭头函数将方法名视为变量?

ii - 在下面的代码中,this的值是什么?与问题1一样,这个this是指窗口对象还是类?

class Foo extends React.Component {
  constructor() {
  }
  clickhandler = () => {
    console.log("you clicked me!!")
  }
  render() {
    return( 
    <div>
      <button onClick={this.clickhandler}> // => CALLBACK

3- 在这里,我看到事件处理程序是一个内联函数,但是看起来它会因为结尾处的 () 被调用,就像在下面的代码段中一样,有时你会看到只给出函数名而没有括号,它们也应该在那里吗?

class MyComponent extends React.Component {
  showValue() {
    console.log(this.props.value);
  }

  render() {
    return (
      <button onClick={() => this.showValue()}>Do Something</button>
    );
  }
}

-------------------------------------------

showValue() {
    console.log(this.props.value);
  }

  render() {
    return (
      <button onClick={this.showValue}>Do Something</button>
    );
  }


非常长但是很好的问题。 - Jalal
谢谢,但是一旦您仔细阅读它,它只是三个类似主题的示例,所以希望不会太长。 - j obe
每个帖子请只提一个问题。 - Bergi
@Bergi 当然,这些似乎密切相关,所以这次我把它们放在了一个帖子里。 - j obe
1
@jobe 3与1和2没有直接关系,应该作为一个单独的问题。 - Estus Flask
@estus 说得好,下次注意。 - j obe
2个回答

5
为什么语法是clickHandler = () => 而不是clickHandler() = >? foo() => ...语法对于ES6类来说不是有效的,这个概念也没有意义。foo() {...}是原型方法的语法糖:
function Class() {}
Class.prototype.foo = function () {
  // this instanceof Class === true
}

如果 Class.prototype.foo 是箭头函数,这个方法将行不通;this会从定义 Class 的作用域中检索出。
// this === undefined

Class.prototype.foo = () => {
  // this === undefined
}

foo = () => ...类字段语法,这是一个处于ES6规范第三阶段的提案。

class Class {
  foo = () => {
    // this instanceof Class === true 
  }
}

是语法糖,其含义为:

class Class {
  constructor() {
    // this instanceof Class === true 
    this.foo = () => {
      // this instanceof Class === true 
    }
  }
}

我看过一些教程,其中有些说箭头函数从全局/窗口作用域继承,因此this的值将是窗口,但也有一些教程说它将从类上下文/作用域中继承this - 哪一个是正确的?

箭头函数获取词法this来自封闭的作用域。如果一个箭头定义在全局作用域中,则thiswindow,在ES模块作用域中是undefined

在上面的示例中,箭头函数定义在类构造函数作用域中,this是类实例。

在这里,我看到事件处理程序是内联函数,但看起来它由末尾的()调用,有时在后续片段中,您可以看到只给出函数名而不带括号,它们不应该也在那里吗?

期望将回调函数作为onClick prop传递。 this.showValue()调用一个函数并从中返回一个值。除非这个值也是一个函数,像onClick={this.showValue()}这样直接调用方法是不正确的。

onClick={this.showValue}将类方法作为回调传递。由于showValue是原型方法,没有绑定到正确的this上下文,回调将在错误的上下文中执行(问题在这个问题中解释),而onClick={() => this.showValue()}将包装函数作为回调传递,以使用正确的上下文执行showValue


我实际上有一个类似的问题,关于第三个问题,想知道我们应该如何传递参数 onClick={this.showValue} - Isaac
哪个参数?问题中的示例没有包含参数。如果除了点击事件的event对象之外还应该有其他内容,那么它应该被包装在一个函数中,无论如何都要这样做,onClick={e => this.showValue('foo')} - Estus Flask
@estus 我有一个关于我提供的具体示例的问题仍然让我困惑,这里的封闭范围是什么?是类还是窗口?正如我所说,我在教程中看到过两种答案。 - j obe
我更新了答案以使其更具体。代码包含关于在这种特定情况下this是什么的注释。至于foo = () =>...类方法,this是类实例,答案解释了原因。 - Estus Flask
1
因为 this.showValue 是一个函数,而 this.showValue() 则是从这个函数返回的值,即 undefined。你不需要将 undefined 传递给 onClick。你可以将它们都记录在控制台中进行检查。当然,在点击发生时你需要使用括号来调用该函数。 - 你不需要调用它,React会为你完成。回调是一个稍后将会 被调用回来 的函数。你传递回调的地方负责调用它。 - Estus Flask
显示剩余7条评论

1

1- this 指当前的作用域。在 MyClass 中,this 指类实例。 因此答案都是正确的。在全局作用域中,this 指窗口,在 myclass 内部,this 指该类。

2- 正如您可能注意到的那样,在箭头函数中不需要使用类绑定函数,因此它们的语法不同。

3- onClick={() => this.showValue()}>Do Something</button>:每次点击事件触发时创建一个函数。当您希望从组件渲染到事件处理程序传递额外参数时,这非常有用。

onClick={() => this.showValue(event, id, name)}>Do Something</button>

但是对于通常情况下的 onClick={this.showValue}>Do Something</button>,您可以在每次触发事件时调用处理程序而无需创建新函数。

但是如何确定处理程序呢? 通过绑定,this 可以识别您的作用域内的内容,因为您在其中调用了 showValue


不,this关键字从不指代“作用域”。 - Bergi
@Bergi,您能否请解释一下?对于问题1,我已经看到了关于class context和window的答案。 - j obe
@jobe https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this。Estus的答案是正确的,在类的构造函数中创建的箭头函数中,`this`指的是该实例。 - Bergi
@Bergi 哦,好的,那很有道理,但我想知道在哪种情况下我看到过一些教程,他们说箭头函数的词法值'this'将是window,这就是我感到困惑的地方。 - j obe
@Jalal,仍然说“refers to current scope”是错误的(至少术语上是这样)。 - Bergi
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接