web-dev-qa-db-ja.com

ES6メソッドを呼び出すときのバインディングコンテキスト。コールバックとして呼び出されたメソッド内からオブジェクトにアクセスする方法は?

ES6のクラスの構文に頭を回そうとしています。同時に、ボニーアイゼンマンの学習を通じてファブリックのネイティブを学習するReactネイティブ。

コールバックがクラスの「メソッド」である場合、コールバックでthisにアクセスする問題に遭遇しました。 StackOverflowでコールバックの字句thisに関する問題が何度も発生していることを知っています。たとえば コールバック内で正しい「this」コンテキストにアクセスする方法は? です。

オンラインでの私の研究に基づいて、解決策を見つけました。しかし、これがES6でこれを行う正しい方法であるかどうかはわかりません。

以下のことを試したところ、私の問題が発生しました。

_class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Zip: ''
    };
  }

  _handleTextChange(event) {
    console.log(event.nativeEvent.text);
    this.setState({Zip: event.nativeEvent.text})
  }

  render() {
    return (
      <TextInput
        style={styles.input}
        onSubmitEditing={this._handleTextChange}/>
    );
  }
}
_

(私は本の例からほんの少し変更しただけで、ES6クラスの構文とRequireではなくimport/export構文に一致しています。)

これを行うと、__handleTextChange_のthisが未定義になります(undefinedのプロパティ 'setState'を読み取れません)。びっくりしました。他のOO=言語から来て、私はこのメソッドが静的メソッドであるかのように動作しているように解釈しています。

これを解決するには、クラスメソッドをスキップし、矢印表記を使用します。 onSubmitEditing={event => this.setState({name: event.nativeEvent.text})}。うまくいきます。私はそれに問題や混乱はありません。

私は本当にクラスメソッドを呼び出す方法を考え出したいと思っています。かなりの調査の後、私は次のようにしてそれを機能させることができました:onSubmitEditing={this._handleTextChange.bind(this)}。 JavaScriptの基本的な側面を誤解しているかもしれません(私はJSの初心者です)が、これは私にはまったく正気ではないようです。オブジェクトを明示的にバインドせずに、メソッド内からオブジェクトのコンテキストにアクセスする方法は本当にありませんか?呼び出された時点で、それは独自のメソッドですか?

また、コンストラクタに_var self = this;_を追加し、_self.setState_で__handleTextChange_を呼び出してみました。しかし、それがうまくいかなかったことを見つけるのにそれほど驚いてはいませんでした。

コールバックとして呼び出されたときに、そのメソッドの1つからオブジェクトにアクセスする正しい方法は何ですか?

13
rod

React.createClass(ES5)のクラス作成方法には、すべてのメソッドを自動的にthisにバインドする組み込み機能があります。しかし、ES6にclassesを導入し、React.createClassを移行している間、他のクラスでこの機能に慣れていないJavaScript開発者にとっては少し混乱する可能性があるか、Reactを他のクラスに。

したがって、彼らはこれをReactのクラスモデルに組み込まないことにしました。必要に応じて、コンストラクタでメソッドを明示的に事前バインドすることもできます

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Zip: ''
    };
    this._handleTextChange = this._handleTextChange.bind(this); //Binding to `this`
  }

  _handleTextChange(event) {
    console.log(event.nativeEvent.text);
    this.setState({Zip: event.nativeEvent.text})
  }

ただし、この事前バインドを回避する簡単な方法は常にあります。うん!了解しました。矢印機能。

class WeatherProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      Zip: ''
    };
  }

  _handleTextChange = event => {
    console.log(event.nativeEvent.text);
    this.setState({Zip: event.nativeEvent.text})
  }

  render() {
    return (
      <TextInput
        style={styles.input}
        onSubmitEditing={this._handleTextChange}/>
    );
  }
}

ところで、これはすべてReactに関するものです。 ES6クラスは常に、オブジェクトをそれ自体のメソッドに明示的にバインドせずに、メソッド内からオブジェクトのコンテキストにアクセスする方法を備えています。

class bindTesting {
  constructor() {
    this.demo = 'Check 1';
  }

  someMethod() {
    console.log(this.demo);
  }

  callMe() {
    this.someMethod();
  }
}

let x = new bindTesting();
x.callMe(); //Prints 'Check 1';

しかし、JSX式で呼び出した場合、「チェック1」は出力されません。

編集:: @Okaが述べたように、クラス本体の矢印関数はES7 +機能であり、コンパイラー/バベルのようなポリフィルで使用できます。この機能をサポートするトランスパイラーを使用していない場合は、上記のようにthisにバインドするか、このような新しいBaseComponentを作成することができます(これは悪い考えです)

class BaseComponent extends React.Component {
 _bind(...methods) {
  methods.forEach( (method) => this[method] = this[method].bind(this) );
 }
}

class ExampleComponent extends BaseComponent {
 constructor() {
  super();
  this._bind('_handleTextChange', '_handleClick');
 }
 // ...
}
14
Prasanth

ES6からの脱却、およびReactしばらくの間、通常の古いJavaScriptでは、オブジェクトのメソッドをその周りに渡すと、オブジェクトではなく関数自体への参照になりますおよび関数。

すべての呼び出し関数は、通常の関数を呼び出すことにより、暗黙のthisの使用を選択できます。また、.call.apply、または.bindを使用してコンテキストを変更することもできます。 。

var O = {
  foo: function () {
    console.log(this);
  },
  bar: 51
};

O.foo(); // `this` is O, the implicit context, inferred from the object Host

var foo = O.foo;

foo(); // `this` is is the global object, or undefined in Strict Mode

何が起こっているかというと、_handleTextChangefunctionをイベントエミッタに渡しており、しばらくしてから実行されます。イベントエミッターは、関数がメソッドとして受信されたことを認識していません。実行するだけです。

var O = {
  foo: function (event) {
    console.log(this);
    console.log(event);
  },
  bar: 51
};

function invoke (func) { // the function reference held by O.foo is now held by `func`
  func('some param'); // it has no idea as to what the contextual `this` is bound to, maybe its implicitly global, maybe its explicitly bound, maybe its undefined
}

invoke(O.foo);

invoke(O.foo.bind(O)); // A copy of the function with the context explicitly bound

コンテキスト共有を見てください:

function foo () {
  console.log(this.a);
}

// Both object hold references to the foo function
var O = { foo : foo, a : 5 },
    O2 = { bar : foo, a : 1 };

O.foo(); // implicit `this` from O
O2.bar(); // implicit `this` from O2

var Z = { a : 23 };

O.foo.call(Z); // explicit `this` from Z

これらの例では、任意に複雑になる可能性があります。

6
Oka

JavaScriptの基本的な側面を誤解しているかもしれません(私はJSの初心者です)が、これは私にはまったく正気ではないようです。オブジェクトを明示的にバインドせずに、メソッド内からオブジェクトのコンテキストにアクセスする方法は本当にありませんか?呼び出された時点で、それは独自のメソッドですか?

まあ、「メソッド」はJavaScriptのオブジェクトによって所有されていない。すべての関数はファーストクラスの値(オブジェクト)であり、オブジェクトまたはクラスの「一部」ではありません。

this value のバインディングは、関数の呼び出し方法に応じて、関数の呼び出し時に発生します。メソッド表記の関数呼び出しでは、thisが受信オブジェクトに設定され、イベントリスナー呼び出しではthisが現在のイベントターゲットに設定され、通常の関数呼び出しではundefined

インスタンスのメソッドにアクセスしている場合、それは実際に標準プロトタイプ継承プロパティアクセスであり、関数が生成されます。この関数が呼び出され、レシーバーに動的にバインドされます。

イベントリスナーであるインスタンスのメソッドとして関数を呼び出す場合は、これを行う関数、つまり「バインドされた」メソッドを明示的に作成する必要があります。 ES6では、通常、矢印表記が優先されます。

3
Bergi