アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this
, arguments
, super
, new.target
を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。
構文
基本的な構文
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // 上記の式は、次の式と同等です: => { return expression; } // 引数が 1 つしかない場合、丸括弧 () の使用は任意です: (singleParam) => { statements } singleParam => { statements } // 引数がない場合、丸括弧を書かねばいけません: () => { statements }
高度な構文
// object リテラル式を返す場合は、本体を丸括弧 () で囲みます: params => ({foo: bar}) // 残余引数 と デフォルト引数 をサポートしています (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // 引数リスト内の分割代入もサポートしています var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
説明
hacks.mozilla.org の "ES6 In Depth: Arrow functions" も参照してください。
2 つの理由から、アロー関数が導入されました。1 つ目の理由は関数を短く書きたいということで、2 つ目の理由は this
を束縛したくない、ということです。
関数の短縮形
var elements = [ 'Hydrogen', 'Helium', 'Lithium', 'Beryllium' ]; elements.map(function(element) { return element.length; }); // this statement returns the array: [8, 6, 7, 9] // The regular function above can be written as the arrow function below elements.map((element) => { return element.length; }); // [8, 6, 7, 9] // When there is only one parameter, we can remove the surrounding parenthesies: elements.map(element => { return element.length; }); // [8, 6, 7, 9] // When the only statement in an arrow function is `return`, we can remove `return` and remove // the surrounding curly brackets elements.map(element => element.length); // [8, 6, 7, 9] // In this case, because we only need the length property, we can use destructuring parameter: // Notice that the string `"length"` corresponds to the property we want to get whereas the // obviously non-special `lengthFooBArX` is just the name of a variable which can be changed // to any valid variable name you want elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9] // This destructuring parameter assignment can be written as seen below. However, note that there // is no specific `"length"` to select which property we want to get. Instead, the literal name // itself of the variable `length` is used as the property we want to retrieve from the object. elements.map(({ length }) => length); // [8, 6, 7, 9]
this
を束縛しない
アロー関数が登場するまでは、関数ごとに自身の this
の値を定義していました(コンストラクタでは新しいオブジェクト、strict モード の関数呼び出しでは undefined、「オブジェクトのメソッド」として呼び出された関数ではそのときのオブジェクト、など)。これは、オブジェクト指向プログラミングをする上で煩わしいということが分かりました。
function Person() { // Person() のコンストラクタは、自分のインスタンスを `this` として定義する。 this.age = 0; setInterval(function growUp() { // 非 strict モードでは、growUp() 関数は `this` をグローバルオブジェクトとして定義する。 // Person() コンストラクタが定義した `this` とは違う。 this.age++; }, 1000); } var p = new Person();
ECMAScript 3/5 では、この問題は this
の値をスコープ内の変数に代入することで解決できました。
function Person() { var that = this; that.age = 0; setInterval(function growUp() { // このコールバックは、期待されるオブジェクトの値を // `that` 変数で参照する。 that.age++; }, 1000); }
あるいは、適切な this
の値を対象の関数(上の例では growUp()
関数)に渡すように、束縛関数を作成することもできました。
アロー関数自身は this
を持ちません。レキシカルスコープの this
値を使います。つまり、アロー関数内の this
値は通常の変数検索ルールに従います(スコープに this
値がない場合、その一つ外側のスコープで this
値を探します)。
そのため、次のコードで setInterval
に渡される関数の this
の値は、外部関数の this
と同じ値になります:
function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| は person オブジェクトを適切に参照します。 }, 1000); } var p = new Person();
strict モードとの関連
this
がレキシカルなものなら、strict モードの this
に関する規則は無視されます。
var f = () => { 'use strict'; return this; }; f() === window; // またはグローバルオブジェクト
他の strict モードの規則は通常通り適用されます。
call や apply からの呼び出し
アロー関数は自身で this
を持たないので、call()
や apply()
メソッドは引数しか渡せません。thisArg
は無視されます。
var adder = { base: 1, add: function(a) { var f = v => v + this.base; return f(a); }, addThruCall: function(a) { var f = v => v + this.base; var b = { base: 2 }; return f.call(b, a); } }; console.log(adder.add(1)); // 2 を出力する console.log(adder.addThruCall(1)); // やはり 2 を出力する
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); // 6
多くの場合、残余引数が arguments
オブジェクトの代わりに使えます。
function foo(n) { var f = (...args) => args[0] + n; return f(10); } foo(1); // 11
メソッドとして使われるアロー関数
前に述べたように、アロー関数式は非メソッド型の関数に最もよく合っています。これをメソッドとして使った時のことを見てみましょう:
'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
を持ちません。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' } });
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
キーワードはアロー関数内で使用できません(内部で入れ子になった関数が許可されている場合を除く)。結果として、アロー関数はジェネレーターとして使用できません。
関数の Body 部分
アロー関数は、「簡潔文体 (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 = () => 1; // SyntaxError: expected expression, got '=>'
その上で、下記の例は、括弧を使って、更に引数の内側で改行を使うことで、綺麗で柔らかなコードに修正できることを確認しています。
var func = ( a, b, c ) => ( 1 ); // no SyntaxError thrown
解析の順序
アロー関数内のアロー(矢印)はオペレーターではないですが、アロー関数は通常の関数と異なり、オペレーターを引き継いだ特別な解析ルールを持ちます。
let callback; callback = callback || function() {}; // ok callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments callback = callback || (() => {}); // ok
さらなる例
// 空のアロー関数は undefined を返します let empty = () => {}; (() => 'foobar')(); // "foobar" を返します // (this is an Immediately Invoked Function Expression // see 'IIFE' in glossary) 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);
仕様
仕様書 | 状態 | コメント |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) Arrow Function Definitions の定義 |
標準 | 初期定義 |
ECMAScript Latest Draft (ECMA-262) Arrow Function Definitions の定義 |
ドラフト |
ブラウザーの実装状況
デスクトップ | モバイル | サーバー | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Arrow functions | Chrome 完全対応 45 | Edge 完全対応 あり | Firefox
完全対応
22
| IE 未対応 なし | Opera 完全対応 32 | Safari 完全対応 10 | WebView Android 完全対応 45 | Chrome Android 完全対応 45 | Edge Mobile 完全対応 あり | Firefox Android
完全対応
22
| Opera Android 完全対応 32 | Safari iOS 完全対応 10 | Samsung Internet Android 完全対応 5.0 | nodejs 完全対応 あり |
Trailing comma in parameters | Chrome 完全対応 58 | Edge ? | Firefox 完全対応 52 | IE 未対応 なし | Opera 完全対応 45 | Safari ? | WebView Android 完全対応 58 | Chrome Android 完全対応 58 | Edge Mobile ? | Firefox Android 完全対応 52 | Opera Android 完全対応 45 | Safari iOS ? | Samsung Internet Android 完全対応 7.0 | nodejs 完全対応 あり |
凡例
- 完全対応
- 完全対応
- 未対応
- 未対応
- 実装状況不明
- 実装状況不明
- 実装ノートを参照してください。
- 実装ノートを参照してください。