アロー関数またはラムダは、ES 6 で導入されました。最小限の構文の優雅さは別として、最も顕著な機能上の違いは、アロー関数内のスコープです。 this
正規の関数式では、キーワードは呼び出されたコンテキストthis
に基づいて異なる値にバインドされます。
これは、アローthis
関数が定義されたスコープ (親スコープ) から閉じ、this
どこでどのように呼び出されても変更されないことを意味します。
オブジェクトのメソッドとしてのアロー関数の制限
// this = global Window
let objA = {
id: 10,
name: "Simar",
print () { // same as print: function()
console.log(`[${this.id} -> ${this.name}]`);
}
}
objA.print(); // logs: [10 -> Simar]
objA = {
id: 10,
name: "Simar",
print: () => {
// Closes over this lexically (global Window)
console.log(`[${this.id} -> ${this.name}]`);
}
};
objA.print(); // logs: [undefined -> undefined]
レギュラーでメソッド定義した場合、objA.print()
メソッド呼び出しに対してはちゃんと解決して動作しましたが、アロー関数として定義すると失敗しました。これは、オブジェクト ( ) のメソッドとして呼び出される通常の関数では、オブジェクト自体であるためです。print()
function
this
objA
=>
this
objA
ただし、アロー関数の場合、それが定義された外側のスコープ (この場合はグローバル/ウィンドウ)this
の the にレキシカルにバインドさthis
れ、メソッド on としての呼び出し中も同じままobjA
です。
オブジェクトのメソッド内の通常の関数よりもアロー関数の利点がありますが、それthis
は定義時に固定およびバインドされることが予想される場合のみです。
/* this = global | Window (enclosing scope) */
let objB = {
id: 20,
name: "Paul",
print () { // Same as print: function()
setTimeout( function() {
// Invoked async, not bound to objB
console.log(`[${this.id} -> ${this.name}]`);
}, 1)
}
};
objB.print(); // Logs: [undefined -> undefined]'
objB = {
id: 20,
name: "Paul",
print () { // Same as print: function()
setTimeout( () => {
// Closes over bind to this from objB.print()
console.log(`[${this.id} -> ${this.name}]`);
}, 1)
}
};
objB.print(); // Logs: [20 -> Paul]
objB.print()
メソッドが[${this.id} -> {this.name}]をコールバックとして非同期にprint()
呼び出す関数として定義されている場合、アロー関数がコールバックとして使用された場合に正しく解決されます。ただし、コールバックが通常の関数として定義されている場合は失敗しました。console.log(
)
setTimeout
this
objB
これは=>
、 に渡されたアロー関数が、その親からレキシカルsetTimeout(()=>..)
に閉じているためです。つまり、それを定義した の呼び出しです。つまり、 に渡されたアロー関数は、 の呼び出しがそれ自体だったため、 にバインドされています。this
objB.print()
=>
setTimeout(()==>...
objB
this
objB.print()
this
objB
Function.prototype.bind()
正しい にバインドすることで、通常の関数として定義されたコールバックを簡単に機能させることができますthis
。
const objB = {
id: 20,
name: "Singh",
print () { // The same as print: function()
setTimeout( (function() {
console.log(`[${this.id} -> ${this.name}]`);
}).bind(this), 1)
}
}
objB.print() // logs: [20 -> Singh]
ただし、アロー関数は便利であり、非同期コールバックの場合はエラーが発生しにくくなりますthis
。関数の定義時に、アロー関数を取得してバインドする必要があることがわかっている場合です。
this
呼び出し間で変更する必要があるアロー関数の制限
いつでも、this
呼び出し時に変更できる関数が必要ですが、アロー関数は使用できません。
/* this = global | Window (enclosing scope) */
function print() {
console.log(`[${this.id} -> {this.name}]`);
}
const obj1 = {
id: 10,
name: "Simar",
print // The same as print: print
};
obj.print(); // Logs: [10 -> Simar]
const obj2 = {
id: 20,
name: "Paul",
};
printObj2 = obj2.bind(obj2);
printObj2(); // Logs: [20 -> Paul]
print.call(obj2); // logs: [20 -> Paul]
上記のいずれもアロー関数const print = () => { console.log(
[${this.id} -> {this.name}]);}
では機能しthis
ません。変更できないためthis
、それが定義された外側のスコープ (グローバル / ウィンドウ) にバインドされたままになります。
これらのすべての例では、関数が宣言された後に作成された異なるオブジェクト (obj1
および) を使用して、同じ関数を次々に呼び出しました。obj2
print()
これらは不自然な例ですが、もう少し実際の例について考えてみましょう。reduce()
で動作するメソッドと同様のメソッドを作成する必要がある場合、呼び出しコンテキスト、つまり呼び出された配列arrays
から推論する必要があるため、ラムダとして定義することはできません。this
このため、コンストラクターthis
関数は宣言時に設定できないため、コンストラクター関数をアロー関数として定義することはできません。コンストラクター関数がキーワードで呼び出されるたびnew
に、新しいオブジェクトが作成され、その特定の呼び出しにバインドされます。
また、フレームワークまたはシステムが動的コンテキストで後で呼び出されるコールバック関数を受け入れる場合、呼び出しごとに変更する必要がある可能性があるため、this
アロー関数を使用できません。this
この状況は、DOM イベント ハンドラーでよく発生します。
'use strict'
var button = document.getElementById('button');
button.addEventListener('click', function {
// web-api invokes with this bound to current-target in DOM
this.classList.toggle('on');
});
var button = document.getElementById('button');
button.addEventListener('click', () => {
// TypeError; 'use strict' -> no global this
this.classList.toggle('on');
});
これは、 Angular 2+やVue.jsthis
などのフレームワークで、呼び出しがバインディング関数のフレームワークによって管理されるため、テンプレート コンポーネント バインディング メソッドが通常の関数/メソッドであることを期待する理由でもあります。(Angular は Zone.js を使用して、ビュー テンプレート バインディング関数の呼び出しの非同期コンテキストを管理します。)
一方、Reactでは、コンポーネントのメソッドをイベント ハンドラーとして渡したい場合、たとえば のように、呼び出しごとにアロー関数として<input onChange={this.handleOnchange} />
定義する必要があります。handleOnchanage = (event)=> {this.props.onInputChange(event.target.value);}
これは、レンダリングされた DOM 要素の JSX を生成したコンポーネントと同じインスタンスにする必要があります。
この記事は、Medium の出版物にも掲載されています。この記事が気に入った場合、またはコメントや提案がある場合は、拍手するか、 Mediumにコメントを残してください。