2022年1月1日 JavaScript (JS Primer) メソッドとコールバック関数とArrow Function
コールバック関数はcallback()のようにただの関数として呼び出されます。
つまり、コールバック関数として呼び出すとき、この関数にはベースオブジェクトはありません。 そのためcallback関数のthisはundefinedとなります。
// `callback`関数を受け取り呼び出す関数 const callCallback = (callback) => { // `callback`を呼び出す実装 }; const obj = { method() { callCallback(function() { // ここでの `this` は`callCallback`の実装に依存する // `callback()`のように単純に呼び出されるなら`this`は`undefined`になる // `Function#call`などを使って特定のオブジェクトを指定するかもしれない // この問題を回避するために`const that = this`のような一時変数を使う }); } };
- Arrow Functionにおける
this
は呼び出し方の影響を受けません。 つまり、コールバック関数がどのように呼ばれるかという実装についてを考えることなくthis
を扱えます。
const Prefixer = { prefix: "pre", prefixArray(strings) { return strings.map((str) => { // `Prefixer.prefixArray()` と呼び出されたとき // `this`は常に`Prefixer`を参照する return this.prefix + "-" + str; }); } }; const prefixedStrings = Prefixer.prefixArray(["a", "b", "c"]); console.log(prefixedStrings); // => ["pre-a", "pre-b", "pre-c"]
メモ
- JavaScriptでは、関数を定義するためにfunctionキーワードを使います。 functionからはじまる文は関数宣言と呼びます。
- 関数宣言はfunctionから始まります。
- 関数名に()をつけることで、関数としてまとめた処理を呼び出します。
- 関数はオブジェクトのため、変数に代入できます。その場合も()を付けることで呼び出すことができます。
- 関数が値として扱える(変数に代入したりできる)ことをファーストクラス(ファンクション)と呼びます。
- ArrowFunctionは無名関数にのみ使用できます。
- コールバック関数にはベースオブジェクトがありません。
- コールバック関数に
this
を定義する場合はArrow Functionを使用しなければなりません。
Arrow Functionはthis
をbindできない
Arrow Functionで定義した関数ではthisを持つことが出来ないためcall
、apply
、bind
を使ったthis
の指定は単に無視されます。
これまで学んだようにArrowFunctionにthisが渡す暗黙的な値はありません。そのArrowFunctionに対して call
apply
bind
で this
の値を指定する事はできないのは当然ですね。
const fn = () => { return this; }; // Scriptコンテキストの場合、スクリプト直下のArrow Functionの`this`はグローバルオブジェクト console.log(fn()); // グローバルオブジェクト // callで`this`を`{}`にしようとしても、`this`は変わらない console.log(fn.call({})); // グローバルオブジェクト
- Arrow Functionのthisが参照する「自身の外側のスコープにあるもっとも近い関数のthisの値」はcallメソッドで変更できます。
const obj = { method() { const arrowFunction = () => { return this; }; return arrowFunction(); } }; // 通常の`this`は`obj.method`の`this`と同じ console.log(obj.method()); // => obj // `obj.method`の`this`を変更すれば、Arrow Functionの`this`も変更される console.log(obj.method.call("THAT")); // => "THAT"
ここではArrowFunctionに対してcall
を使っていないところがポイントです。obj.method
に対してcall
を使っているのでthis
の値を指定する事ができています。
まとめ
thisは状況によって異なる値を参照する性質を持ったキーワードです。
実行コンテキストやstrict modeなどによって結果が異なり、混乱の元となります。 そのため、メソッドではない通常の関数においては
this
を使うべきではありません。メソッドの中でコールバック関数を使う際は、静的に
this
の値が決まるようにArrowFunctionで書きましょう。this
が指し示すのはベースオブジェクトである事は揺るぎないです。
ES2015の仕様編集者であるAllen Wirfs-Brock氏もただの関数においては
this
を使うべきではないと述べている。
https://twitter.com/awbjs/status/938272440085446657
参考
JS primer メソッドとコールバック関数とArrow Function