--- title: アロー関数式 slug: Web/JavaScript/Reference/Functions/Arrow_functions tags: - ECMAScript 2015 - Functions - Intermediate - JavaScript - Language feature - Reference translation_of: Web/JavaScript/Reference/Functions/Arrow_functions ---
アロー関数式は、従来の 関数式の簡潔な代替構文ですが、制限があり、すべての場面で使用することができるわけではできません。
相違点と制限事項:
this
や super
への結びつけを持たないので、メソッド
として使用することはできません。arguments
や new.target
キーワードがありません。call
、apply
、bind
のような、一般にスコープの設定のためのメソッドには適していません。yield
を使用することはできません。「従来の関数」を分解して、最もシンプルな「アロー関数」に段階的に変えていきましょう。
注: 途中の各ステップは有効な「アロー関数」です。
// 伝統的な関数 function (a){ return a + 100; } // アロー関数に分解 // 1. "function" という語を削除し、引数と本体の開始中括弧の間に矢印を配置する (a) => { return a + 100; } // 2. 本体の中括弧を削除と "return" という語を削除 -- return は既に含まれています。 (a) => a + 100; // 3. 引数の括弧を削除 a => a + 100;
上記の通り、 { 中括弧 } と ( 括弧 ) と "return" は省略可能ですが、必要になる場合もあります。
例えば、複数の引数や引数なしの場合、引数の周りの括弧を入れなおす必要があります。
// 従来の関数 function (a, b){ return a + b + 100; } // アロー関数 (a, b) => a + b + 100; // 従来の関数 (引数なし) let a = 4; let b = 2; function (){ return a + b + 100; } // アロー関数 (引数なし) let a = 4; let b = 2; () => a + b + 100;
同様に、本文に処理の追加の行が必要な場合は、中括弧に加えて "return" を入れなおす必要があります (アロー関数は "return" する場所や返値を魔法のように推測できるわけではありません)。
// 従来の関数 function (a, b){ let chuck = 42; return a + b + chuck; } // アロー関数 (a, b) => { let chuck = 42; return a + b + chuck; }
そして最後に、名前付き関数については、変数のようにアロー関数式を扱います。
// 従来の関数 function bob (a){ return a + 100; } // アロー関数 let bob = a => a + 100;
引数が単一の場合。単純な式ならば return は不要です。
param => expression
引数が複数の場合は括弧が必要です。単純な式ならば return は不要です。
(param1, paramN) => expression
複数行の文ならば、本体の中括弧と return が必要です。
param => { let a = 1; return a + param; }
引数が複数の場合は括弧が必要です。複数行の文ならば、本体の中括弧と return が必要です。
(param1, paramN) => { let a = 1; return a + param1 + paramN; }
オブジェクトリテラル式を返す場合は、式の周りに括弧が必要です。
params => ({foo: "a"}) // オブジェクト {foo: "a"} を返す
残余引数に対応しています。
(a, b, ...r) => expression
デフォルト引数に対応しています。
(a=400, b=20, c) => expression
引数の分割代入に対応しています。
([a, b] = [10, 20]) => a + b; // result is 30 ({ a, b } = { a: 10, b: 20 }) => a + b; // result is 30
前に述べたように、アロー関数式は非メソッド型の関数に最もよく合っています。これをメソッドとして使った時のことを見てみましょう。
'use strict'; var obj = { // 新しいスコープを作成しない i: 10, b: () => console.log(this.i, this), c: function() { console.log(this.i, this); } } obj.b(); // prints undefined, Window {...} (or the global object) obj.c(); // prints 10, Object {...}
アロー関数は自身の this
を持ちません。{{jsxref("Object.defineProperty()")}} を使った他の例です。
'use strict'; var obj = { a: 10 }; Object.defineProperty(obj, 'b', { get: () => { console.log(this.a, typeof this.a, this); // undefined 'undefined' Window {...} (or the global object) return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined' } });
call
、apply
、bind
の各メソッドは、アロー関数にはふさわしくありません。これらは異なるスコープ内でメソッドを実行できるようにするために設計されているものです。アロー関数は、アロー関数が定義されているスコープに基づいて "this" を確立するからです。
例えば、 call
、apply
、bind
は、従来の関数ではそれぞれのメソッドにスコープを確立するので、期待通りに動作します。
// ---------------------- // 従来の関数の例 // ---------------------- // 単純化されたオブジェクトで "this" を持つ var obj = { num: 100 } // "num" を window に設定し、使用されていないことを表す。 window.num = 2020; // yikes! // 単純な従来の関数で "this" を運用する var add = function (a, b, c) { return this.num + a + b + c; } // call var result = add.call(obj, 1, 2, 3) // "obj" としてスコープを確立 console.log(result) // result 106 // apply const arr = [1, 2, 3] var result = add.apply(obj, arr) // "obj" としてスコープを確立 console.log(result) // result 106 // bind var result = add.bind(obj) // "obj" としてスコープを確立 console.log(result(1, 2, 3)) // result 106
アロー関数では、 add
関数は基本的に window
(グローバル) スコープで作成されているので、 this
は windows だと仮定されます。
// ---------------------- // アロー関数の例 // ---------------------- // 単純化されたオブジェクトで "this" を持つ var obj = { num: 100 } // "num" を window に設定し、どのように扱われるかを見る。 window.num = 2020; // yikes! // アロー関数 var add = (a, b, c) => this.num + a + b + c; // call console.log(add.call(obj, 1, 2, 3)) // result 2026 // apply const arr = [1, 2, 3] console.log(add.apply(obj, arr)) // result 2026 // bind const bound = add.bind(obj) console.log(bound(1, 2, 3)) // result 2026
アロー関数を使用する最大の利点は、 DOM レベルのメソッド (setTimeout、setInterval、addEventListener) で、通常、関数が適切なスコープで実行されることを保証するために、クロージャ、call、apply、bind などが必要でした。
従来の関数の例:
var obj = { count : 10, doSomethingLater : function (){ setTimeout(function(){ // 関数を window スコープで実行 this.count++; console.log(this.count); }, 300); } } obj.doSomethingLater(); // コンソールに "NaN" と表示。 "count" プロパティは window スコープではないため。
アロー関数の例:
var obj = { count : 10, doSomethingLater : function(){ // もちろん、アロー関数はメソッドには向いていない setTimeout( () => { // アロー関数が "obj" 内で作成されるので、それがオブジェクトの "this" と見なされる this.count++; console.log(this.count); }, 300); } } obj.doSomethingLater();
arguments
のバインドがないアロー関数は自身の arguments
オブジェクトを持ちません。そのため、この例では、arguments
は囲っているスコープでの同名変数への参照にすぎません。
var arguments = [1, 2, 3]; var arr = () => arguments[0]; arr(); // 1 function foo(n) { var f = () => arguments[0] + n; // foo は arguments をバインドしている。 arguments[0] は n である return f(); } foo(3); // 3 + 3 = 6
多くの場合、残余引数が arguments
オブジェクトの代わりに使えます。
function foo(n) { var f = (...args) => args[0] + n; return f(10); } foo(1); // 11
new
演算子の使用アロー関数はコンストラクターとして使用できず、new
と共に使うとエラーが発生します。
var Foo = () => {}; var foo = new Foo(); // TypeError: Foo is not a constructor
prototype
プロパティの使用アロー関数には prototype
プロパティはありません。
var Foo = () => {}; console.log(Foo.prototype); // undefined
yield
キーワードの使用yield
キーワードはアロー関数内で使用できません (内部で入れ子になった関数が許可されている場合を除く)。結果として、アロー関数はジェネレーターとして使用できません。
アロー関数は、「簡潔文体 (concise body)」か、もしくはより一般的な「ブロック文体 (block body) 」のどちらかを使用することができます。
簡潔文体 (concise body) においては、単一の式しか記述できないので、その式が暗黙的に return される値となります。しかし、ブロック文体においては、自動的に return はされないので、明示的に return
文を使用する必要があります。
var func = x => x * x; // 簡潔構文の場合、暗黙の "return" があります var func = (x, y) => { return x + y; }; // ブロック文体では、明示的な "return" が必要です
簡潔文体 params => {object:literal}
を使ってオブジェクトリテラルを返そうとしても、期待通りに動作しないことに注意しましょう。
var func = () => { foo: 1 }; // 呼び出した func() は undefined を返す! var func = () => { foo: function() {} }; // SyntaxError: function 文には名前が必要
これは、括弧 ({}) 内のコードが文の列として構文解析されてしまっているからです (つまり、foo
はオブジェクトリテラル内のキーでなく、ラベルとして扱われています)。
オブジェクトリテラルは括弧で囲むのを忘れないでください。
var func = () => ({ foo: 1 });
アロー関数では、括弧とアロー (矢印) の間に改行を入れることができません。
var func = (a, b, c) => 1; // SyntaxError: expected expression, got '=>'
しかし、矢印の後に改行を入れたり、以下のように括弧や中括弧を使用して、コードがきれいで滑らかになるように修正することができます。また、引数同士の間にも改行を入れることができます。
var func = (a, b, c) => 1; var func = (a, b, c) => ( 1 ); var func = (a, b, c) => { return 1 }; var func = ( a, b, c ) => 1; // SyntaxError は発生しない
アロー関数のアロー (矢印) は演算子ではありませんが、アロー関数には特別な解釈ルールがあり、通常の関数とは演算子の優先順位の扱いが異なります。
let callback; callback = callback || function() {}; // ok callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments callback = callback || (() => {}); // ok
// 空のアロー関数は undefined を返します let empty = () => {}; (() => 'foobar')(); // "foobar" を返します // (これは、即時起動型の関数式です。) var simple = a => a > 15 ? 15 : a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b ? a : b; // 簡単な配列のフィルターリング、マッピング等 var arr = [5, 6, 13, 0, 1, 18, 23]; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // [6, 0, 18] var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46] // さらに簡潔な promise チェーン promise.then(a => { // ... }).then(b => { // ... }); // 見た目に解析が簡単な引数なしのアロー関数 setTimeout( () => { console.log('I happen sooner'); setTimeout( () => { // deeper code console.log('I happen later'); }, 1); }, 1);
仕様書 |
---|
{{SpecName('ESDraft', '#sec-arrow-function-definitions', 'Arrow Function')}} |
{{Compat("javascript.functions.arrow_functions")}}