アロー関数式 は、その名のとおり矢印を使って記述し、function 式より短い構文で同様な内容を記述することができます。なおthis
, arguments, super, new.target を束縛しません。また、アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。
構文
基本的な構文
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // 上記の式は、次の式と同等です: (param1, param2, …, paramN) => { 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 materials = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ];materials.map(function(material) { return material.length; }); // [8, 6, 7, 9] materials.map((material) => { return material.length; }); // [8, 6, 7, 9] materials.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 を意味します。そのため、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()
メソッドは引数しか渡せません。this
は無視します。
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 '=>'
解析の順序
アロー関数内のアロー(矢印)はオペレーターではないですが、アロー関数は通常の関数と異なり、オペレーターを引き継いだ特別な解析ルールを持ちます。
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 の定義 |
ドラフト |
ブラウザの実装状況
デスクトップ | モバイル | サーバー | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
基本対応 | 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 完全対応 あり |
凡例
- 完全対応
- 完全対応
- 未対応
- 未対応
- 実装状況不明
- 実装状況不明
- 実装ノートを参照してください。
- 実装ノートを参照してください。
関連項目
- hacks.mozilla.org サイトの "ES6 In Depth: Arrow functions"