ES6函数、箭头函数和ES6类中的'this'

10
class App extends Component {
  constructor(props) {
    ...
  }

  onChange = (e) => this.setState({term: e.target.value})

  onSubmit(e){
    e.preventDefault();
    const api_key = "C1hha1quJAQZf2JUlK";
    const url = `http://api.giphy.com/v1/gifs/search?q=${this.state.term}&api_key=${api_key}`;
  }

  render() {
    return (
      <div>
        <form onSubmit={this.onSubmit}>
          <input value={this.state.term} onChange={this.onChange}/>
          <button>Search!</button>
        </form>
      </div>
    );
  }
}

在类中声明的两种函数类型(onChange和onSubmit)有什么区别?如果我将其声明为ES6类方法,则在const url中引用this.sate会出错,但将其更改为箭头函数则可以解决问题。
我想知道这两种情况下'this'是如何处理的。
另外,如果我想使用相同的onSubmit函数(ES6类方法),但想在调用它时处理this(在表单元素中),该怎么做?
是否使用this.onSubmit.bind(this)?
5个回答

28

重要的是要知道这个语法:

class A {
  method = () => {}
}

只是在类构造函数中创建实例方法的语法糖:

class A {
  constructor() {
    this.method = () => {}
  }
}

注意:这个语法目前还不是 JavaScript 语言的正式部分(目前处于第三阶段),所以你必须使用 转译器,如 Babel 来处理它。
method 内部,this 的值为类 A,因为在构造函数中 this 指向它(由于 箭头函数 继承了它们定义所在作用域的上下文)。

class A {
  constructor() {
    this.method = () => this;
  }
}

const instance = new A();
console.log(instance.method() === instance); // true

在类上定义一个常规(非箭头)函数方法会在类原型(而不是实例)上创建一个方法,但不会设置关于this会是什么(因为在JS中this是动态的并取决于函数的调用方式,而不是定义方式)。

class A {
  method() {}
}

console.log(new A().method === A.prototype.method); // true

如果以这两种方式定义的方法在类实例上被调用(通过 . ),根据当函数作为对象的方法被调用时如何绑定this的规则,this在两种情况下都将指向类实例:

class A {
  constructor() {
    this.methodOnInstance = () => this;
  }
  methodOnPrototype() { return this; }
}

const instance = new A();
console.log(
  instance.methodOnInstance() === instance.methodOnPrototype(), // true
  instance.methodOnPrototype() === instance // true
);

上述两种方法声明之间的一个主要区别是实例方法中的this始终固定为类实例,而类(原型)方法不是(我们可以使用Function.prototype.applyFunction.prototype.call更改它)

class A {
  constructor() {
    this.methodOnInstance = () => this;
  }
  methodOnPrototype() { return this; }
}

const instance = new A();
console.log(
  instance.methodOnInstance() === instance.methodOnPrototype(), // true
  instance.methodOnPrototype.call('new this') === 'new this' // true
);

在事件处理程序中,常见的情况是this发生变化。在这种情况下,事件处理程序调用传递给它的函数,并将上下文绑定到发生事件的元素上(因此覆盖了this的值,使其成为被单击或发生事件的元素)。

在所有(synthetic)DOM事件处理程序中,React也会发生这种情况。

因此,如果我们希望方法的上下文始终指向React组件的实例,则可以使用instance方法。

另一种限制上下文但不使用需要Babel的特殊instance方法语法的方式是通过创建一个新函数来直接从类(原型)方法创建一个实例方法,并绑定上下文(使用Function.prototype.bind):

class A {
  constructor() {
    this.methodOnInstance = this.methodOnPrototype.bind(this);
  }
  methodOnPrototype() { return this; }
}

const instance = new A();
console.log(
  instance.methodOnInstance() === instance.methodOnPrototype(), // true
  instance.methodOnPrototype() === instance // true
);

这使我们能够通过当前可用的工具(ES2017及以下版本)达到与使用特殊实例方法语法相同的结果。

如果出于某种原因,我们想要一个始终绑定到不是该类实例的东西的方法,我们也可以这样做:

class A {
  constructor() {
    this.method = this.method.bind(console);
  }
  method() { return this; }
}

const instance = new A();
console.log(
  instance.method() === console // true
);


1

箭头函数表达式比函数表达式语法更短,并且没有自己的this、arguments、super或new.target。这些函数表达式最适合于非方法函数,并且不能用作构造函数。

箭头函数在词法上绑定它们的上下文,因此this实际上是指向源上下文。

在ES3/4函数声明中,可以通过存储在其他变量中使用this

const that = this;
onSubmit(e){
    e.preventDefault();
    const api_key = "***************";
    const url = `http://api.giphy.com/v1/gifs/search?q=${that.state.term}&api_key=${api_key}`;
  }

0
还有,如果我想要另一种方式呢?比如说,如果我想使用相同的 onSubmit 函数(ES6 类方法),但是想要在调用它时(在表单元素中)处理它,该怎么办?
使用 this.onSubmit.bind(this) 吗?
是的,你必须在构造函数中将方法绑定到组件上。这是因为箭头函数会自动绑定到类中,因此方法的作用域设置在方法中。而 onSubmit 是一个常规函数,尚未绑定,因此方法内部的 this 将引用该函数而不是组件。

0

在 ES6 类方法中,您需要在类的构造函数中使用 bind。实质上,箭头函数会自动为您完成这个过程。

constructor(props) {
  super(props);

  this.onSubmit = this.onSubmit.bind(this);
}

这里更重要的是要注意,我相信箭头函数将在类的每个实例上创建,而ES6类方法将成为类的原型的一部分,并在所有实例之间共享。

0

关键区别在于,在ES5中我们没有自动绑定,这意味着您必须手动绑定事件处理程序函数,以便在react函数内部使用状态或属性。但是在ES6中,它会自动绑定。这就是关键区别。

ES5:最好在构造函数中绑定onSubmit。

//is valid
this.onSubmit = this.onSubmit.bind(this);

onSubmit(e){
    e.preventDefault();
    const api_key = "C1hha1quJAQZf2JUlK";
    const url = `http://api.giphy.com/v1/gifs/search?q=${this.state.term}&api_key=${api_key}`;
  }

ES6:

以下是有效的,因为它自动绑定。

onChange = (e) => this.setState({term: e.target.value})

不,它不会自动绑定。它只是不将函数放在原型上。 - Bergi
好的。我理解“自动绑定”这个术语的含义 :) - Hemadri Dasari
绑定意味着在通用(动态this)方法上使用bind - Bergi

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