JavaScriptの矢印関数を理解する
序章
ECMAScript仕様(ES6)の 2015エディションでは、JavaScript言語に矢印関数式が追加されました。 矢印関数は無名関数式を記述する新しい方法であり、Pythonなどの他のプログラミング言語のラムダ関数に似ています。
矢印関数は、スコープの決定方法や構文の表現方法など、多くの点で従来の関数とは異なります。 このため、矢印関数は、組み込みのイテレータメソッドを使用して配列をループする場合など、関数をパラメータとして高階関数に渡す場合に特に便利です。 。 それらの構文上の省略形により、コードの可読性を向上させることもできます。
この記事では、関数の宣言と式を確認し、従来の関数式と矢印関数式の違いについて学び、矢印関数に関連する字句スコープについて学び、矢印関数で許可されている構文の省略形について説明します。
関数の定義
矢印関数式の詳細を掘り下げる前に、このチュートリアルでは、後で矢印関数の固有の側面をよりよく示すために、従来のJavaScript関数を簡単に確認します。
このシリーズの前半のJavaScriptで関数を定義する方法チュートリアルでは、関数宣言および関数式の概念を紹介しました。 関数宣言は、で記述された名前付き関数です。 function
キーワード。 関数宣言は、コードが実行される前に実行コンテキストにロードされます。 これはhoistingと呼ばれ、宣言する前に関数を使用できることを意味します。
これが例です sum
2つのパラメーターの合計を返す関数:
function sum(a, b) {
return a + b
}
あなたは実行することができます sum
巻き上げによる機能を宣言する前の機能:
sum(1, 2)
function sum(a, b) {
return a + b
}
このコードを実行すると、次の出力が得られます。
Output3
関数自体をログに記録することで、関数の名前を見つけることができます。
console.log(sum)
これにより、関数とその名前が返されます。
Outputƒ sum(a, b) {
return a + b
}
関数式は、実行コンテキストにプリロードされていない関数であり、コードがそれに遭遇したときにのみ実行されます。 関数式は通常、変数に割り当てられ、匿名にすることができます。これは、関数に名前がないことを意味します。
この例では、同じように書きます sum
匿名関数式としての関数:
const sum = function (a, b) {
return a + b
}
これで、匿名関数がに割り当てられました sum
絶え間ない。 宣言される前に関数を実行しようとすると、エラーが発生します。
sum(1, 2)
const sum = function (a, b) {
return a + b
}
これを実行すると、次のようになります。
OutputUncaught ReferenceError: Cannot access 'sum' before initialization
また、関数には名前付き識別子がないことに注意してください。 これを説明するために、に割り当てられた同じ無名関数を記述します sum
、次にログに記録する sum
コンソールへ:
const sum = function (a, b) {
return a + b
}
console.log(sum)
これにより、次のことが表示されます。
Outputƒ (a, b) {
return a + b
}
の値 sum
は無名関数であり、名前付き関数ではありません。
で記述された関数式に名前を付けることができます function
キーワードですが、これは実際には人気がありません。 関数式に名前を付ける理由の1つは、エラースタックトレースのデバッグを容易にするためです。
次の関数について考えてみます。この関数は、 ifステートメントを使用して、関数パラメーターが欠落している場合にエラーをスローします。
const sum = function namedSumFunction(a, b) {
if (!a || !b) throw new Error('Parameters are required.')
return a + b
}
sum();
強調表示されたセクションは関数に名前を付け、関数はまたはを使用します ||
いずれかのパラメーターが欠落している場合にエラーobjectをスローする演算子。
このコードを実行すると、次のようになります。
OutputUncaught Error: Parameters are required.
at namedSumFunction (<anonymous>:3:23)
at <anonymous>:1:1
この場合、関数に名前を付けると、エラーがどこにあるかがすぐにわかります。
矢印関数式は、「太い矢印」構文で記述された無名関数式です(=>
).
書き直し sum
矢印関数構文の関数:
const sum = (a, b) => {
return a + b
}
従来の関数式と同様に、矢印関数は吊り上げられていないため、宣言する前に呼び出すことはできません。 また、これらは常に匿名です。矢印関数に名前を付ける方法はありません。 次のセクションでは、矢印関数と従来の関数の構文上および実際上の違いについて詳しく説明します。
矢印関数の動作と構文
矢印関数には、従来の関数と区別するための動作方法にいくつかの重要な違いがあり、構文上の機能もいくつか強化されています。 最大の機能の違いは、矢印関数には独自の機能がないことです。 this
バインディングまたはプロトタイプであり、コンストラクターとして使用することはできません。 矢印関数は、パラメーターの前後の括弧を省略し、暗黙的な戻りを伴う簡潔な関数本体の概念を追加する機能を付与するため、従来の関数のよりコンパクトな代替手段として作成することもできます。
このセクションでは、これらの各ケースを説明する例を見ていきます。
語彙 this
キーワード this
JavaScriptではトリッキーなトピックと見なされることがよくあります。 記事JavaScriptでこれを理解し、バインドし、呼び出し、適用するは、その方法を説明しています。 this
動作し、どのように this
プログラムがグローバルコンテキストで使用するか、オブジェクト内のメソッドとして使用するか、関数またはクラスのコンストラクターとして使用するか、 DOM イベントハンドラーとして使用するかに基づいて、暗黙的に推測できます。
矢印関数には字句thisがあります。これは、 this
周囲のスコープ(字句環境)によって決定されます。
次の例では、従来の関数と矢印関数の処理方法の違いを示します this
. 以下では printNumbers
オブジェクトには、2つのプロパティがあります。 phrase
と numbers
. オブジェクトにはメソッドもあります。 loop
、を印刷する必要があります phrase
文字列と現在の値 numbers
:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
this.numbers.forEach(function (number) {
console.log(this.phrase, number)
})
},
}
人は期待するかもしれません loop
各反復のループ内の文字列と現在の数値を出力する関数。 ただし、関数を実行した結果、 phrase
実は undefined
:
printNumbers.loop()
これにより、次のようになります。
Outputundefined 1
undefined 2
undefined 3
undefined 4
これが示すように、 this.phrase
は未定義であり、 this
forEachメソッドに渡された無名関数内は printNumbers
物体。 これは、従来の関数がその関数を決定しないためです this
環境の範囲からの値、つまり printNumbers
物体。
古いバージョンのJavaScriptでは、 bind
明示的に設定するメソッド this
. このパターンは、ES6が登場する前の、Reactなどの以前のバージョンのフレームワークでよく見られます。
使用する bind
機能を修正するには:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
// Bind the `this` from printNumbers to the inner forEach function
this.numbers.forEach(
function (number) {
console.log(this.phrase, number)
}.bind(this),
)
},
}
printNumbers.loop()
これにより、期待される結果が得られます。
OutputThe current value is: 1
The current value is: 2
The current value is: 3
The current value is: 4
矢印関数は、これに対処するためのより直接的な方法を提供します。 彼らの以来 this
値は、字句スコープ、で呼び出される内部関数に基づいて決定されます。 forEach
アウターのプロパティにアクセスできるようになりました printNumbers
示されているように、オブジェクト:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
this.numbers.forEach((number) => {
console.log(this.phrase, number)
})
},
}
printNumbers.loop()
これにより、期待される結果が得られます。
OutputThe current value is: 1
The current value is: 2
The current value is: 3
The current value is: 4
これらの例は、 forEach 、 map 、 filter 、reduceなどの組み込み配列メソッドで矢印関数を使用する方が直感的であることを示しています。読みやすく、この戦略が期待に応える可能性が高くなります。
矢印はオブジェクトメソッドとして機能します
矢印関数は、配列メソッドに渡されるパラメーター関数としては優れていますが、レキシカルスコープを使用する方法のため、オブジェクトメソッドとしては効果的ではありません。 this
. 前と同じ例を使用して、 loop
メソッドを作成し、それを矢印関数に変換して、実行方法を確認します。
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop: () => {
this.numbers.forEach((number) => {
console.log(this.phrase, number)
})
},
}
このオブジェクトメソッドの場合、 this
のプロパティとメソッドを参照する必要があります printNumbers
物体。 ただし、オブジェクトは新しい字句スコープを作成しないため、矢印関数はオブジェクトの向こう側で次の値を探します。 this
.
電話する loop()
方法:
printNumbers.loop()
これにより、次のようになります。
OutputUncaught TypeError: Cannot read property 'forEach' of undefined
オブジェクトは字句スコープを作成しないため、矢印関数メソッドは this
外側のスコープ–この例ではWindow。 以来 numbers
プロパティがに存在しません Window
オブジェクト、それはエラーをスローします。 原則として、デフォルトでは従来の関数をオブジェクトメソッドとして使用する方が安全です。
矢印関数にはありません constructor
また prototype
このシリーズの前半のJavaScriptでのプロトタイプと継承の理解チュートリアルでは、関数とクラスには prototype
プロパティ。これは、JavaScriptがクローン作成と継承の青写真として使用するものです。
これを説明するために、関数を作成し、自動的に割り当てられたログを記録します prototype
財産:
function myFunction() {
this.value = 5
}
// Log the prototype property of myFunction
console.log(myFunction.prototype)
これにより、コンソールに次のように出力されます。
Output{constructor: ƒ}
これは、 prototype
プロパティには、 constructor
. これにより、 new
関数のインスタンスを作成するためのキーワード:
const instance = new myFunction()
console.log(instance.value)
これにより、 value
関数を最初に宣言したときに定義したプロパティ:
Output5
対照的に、矢印関数には prototype
財産。 新しい矢印関数を作成し、そのプロトタイプをログに記録してみてください。
const myArrowFunction = () => {}
// Attempt to log the prototype property of myArrowFunction
console.log(myArrowFunction.prototype)
これにより、次のようになります。
Outputundefined
行方不明の結果として prototype
プロパティ、 new
キーワードは使用できず、矢印関数からインスタンスを作成することはできません。
const arrowInstance = new myArrowFunction()
console.log(arrowInstance)
これにより、次のエラーが発生します。
OutputUncaught TypeError: myArrowFunction is not a constructor
これは、前の例と一致しています。矢印関数には独自の関数がないため this
値を指定すると、コンストラクターとして矢印関数を使用できなくなります。
ここに示すように、矢印関数には多くの微妙な変更があり、ES5以前の従来の関数とは動作が異なります。 また、いくつかのオプションの構文上の変更があり、矢印関数の記述がより速く、より冗長ではなくなりました。 次のセクションでは、これらの構文変更の例を示します。
暗黙のリターン
従来の関数の本体は、中括弧を使用してブロック内に含まれています {}
コードが return
キーワード。 以下は、この実装が矢印関数としてどのように見えるかです。
const sum = (a, b) => {
return a + b
}
矢印関数は、簡潔な本体構文、または暗黙のreturnを導入します。 これにより、中括弧と return
キーワード。
const sum = (a, b) => a + b
暗黙のリターンは、で簡潔な1行の操作を作成するのに役立ちます map
, filter
、およびその他の一般的な配列メソッド。 角かっこと両方が return
キーワードは省略できます。 本文を1行のreturnステートメントとして記述できない場合は、通常のブロック本文構文を使用する必要があります。
オブジェクトを返す場合、構文では、オブジェクトリテラルを括弧で囲む必要があります。 それ以外の場合、角かっこは関数本体として扱われ、 return
価値。
これを説明するために、次の例を見つけてください。
const sum = (a, b) => ({result: a + b})
sum(1, 2)
これにより、次の出力が得られます。
Output{result: 3}
単一のパラメーターの前後の括弧の省略
もう1つの便利な構文拡張機能は、関数内の1つのパラメーターの前後から括弧を削除する機能です。 次の例では、 square
関数は1つのパラメーターでのみ動作します。 x
:
const square = (x) => x * x
その結果、パラメーターを囲む括弧を省略でき、同じように機能します。
const square = x => x * x
square(10)
これにより、次のようになります。
Output100
関数がパラメータを受け取らない場合は、括弧が必要になることに注意してください。
const greet = () => 'Hello!'
greet()
呼び出し greet()
次のように動作します:
Output'Hello!'
一部のコードベースは、可能な限り括弧を省略することを選択し、他のコードベースは、特に TypeScript を使用し、各変数とパラメーターに関する詳細情報を必要とするコードベースでは、パラメーターの前後に常に括弧を保持することを選択します。 矢印関数の書き方を決めるときは、貢献しているプロジェクトのスタイルガイドを確認してください。
結論
この記事では、従来の関数と、関数宣言と関数式の違いについて説明しました。 矢印関数は常に匿名であり、 prototype
また constructor
、と一緒に使用することはできません new
キーワード、およびの値を決定します this
語彙スコープを介して。 最後に、単一パラメーター関数の暗黙的な戻りや括弧の省略など、矢印関数で使用できる新しい構文拡張機能について説明しました。
基本的な関数のレビューについては、JavaScriptで関数を定義する方法をお読みください。 JavaScriptでのスコープとホイストの概念の詳細については、 JavaScriptでの変数、スコープ、およびホイストについてを参照してください。