web-dev-qa-db-ja.com

ES6関数、矢印関数、およびES6クラスの「this」

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>
    );
  }
}

クラスで宣言された2つのタイプの関数の違いは何ですか(onChange and onSubmit)。これをES6クラスメソッドとして宣言すると、const urlでthis.sateを参照するとエラーが発生しますが、arrow関数に変更すると修正されます。

どちらの場合も「これ」がどのように正確に処理されるか知りたい

また、どうすれば逆にできますか?たとえば、同じonSubmit関数(ES6クラスメソッド)を使用したいが、(form要素で)呼び出すときにこれを処理したい場合、どうすればよいですか?

this.onSubmit.bind(this)を使用しますか?

6
d_bhatnagar

この構文を知っておくことが重要です。

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

クラスコンストラクターでインスタンスメソッドを作成するための単なる構文糖衣です。

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

注:この構文はまだJavaScript言語の公式部分ではないため( 現在はステージ )、 Babelのようなトランスパイラーを使用して処理する を使用する必要があります。

this内のmethodの値はクラスAです。これは、コンストラクターでthisが指すものであるためです( 矢印関数 =定義されているスコープからコンテキストを継承します):

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

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

クラスで通常の(矢印以外の関数)メソッドを定義すると、クラスのプロトタイプ(インスタンスではない)にメソッドが作成されますが、thisがどのようになるかについてのルールは設定されません(thisはJSで動的であるため)および 関数の定義方法ではなく、関数の呼び出し方法によって異なります )。

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
);

上記の2つのメソッド宣言の主な違いの1つは、インスタンスメソッドのthisalwaysがクラスインスタンスに固定されている一方で、クラス(プロトタイプ)メソッドはしません( Function.prototype.apply または Function.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コンポーネントのインスタンスを指すようにしたい場合は、インスタンスメソッドを使用できます。

コンテキストを制限するが、Babelを必要とする特別なインスタンスメソッド構文を使用しない別の方法は、バインドされたコンテキストを持つクラス(プロトタイプ)メソッドから新しい関数を作成することによって、インスタンスメソッドを自分で直接作成することです( 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
);
11
nem035

矢印関数式の構文は関数式よりも短く、独自のthis、arguments、super、またはnew.targetはありません。これらの関数式は、メソッド以外の関数に最適であり、コンストラクターとして使用することはできません。

矢印関数はそれらのコンテキストを字句的にバインドするため、これは実際には元のコンテキストを参照します。

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}`;
  }
1
void

主な違いは、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})
0
Hemadri Dasari

また、どうすれば逆にできますか?たとえば、同じonSubmit関数(ES6クラスメソッド)を使用したいが、(form要素で)呼び出すときにこれを処理したい場合、どうすればよいですか?

This.onSubmit.bind(this)を使用していますか?

はい、コンストラクターのコンポーネントにメソッドをバインドする必要があります。これは、矢印関数が自動的にクラスにバインドされるため、このスコープがメソッドで設定されるためです。 onSubmitはまだバインドされていない通常の関数であるため、メソッド内のthisはコンポーネントではなく、関数を参照します。

0
Adarsh

ES6クラスメソッドを使用して、クラスのコンストラクターでbindを使用する必要があります。本質的に、矢印関数はこれを自動的に行います。

constructor(props) {
  super(props);

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

ここで注意すべきより重要なことは、ここでの矢印関数はクラスの各インスタンスで作成され、ES6クラスメソッドがクラスのプロトタイプの一部になり、すべてのインスタンス間で共有されると信じていることです。

0
John Halbert