From df60a2aedb4549b7e091a57814ef247914c93d27 Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Mon, 26 Apr 2021 01:37:25 +0900 Subject: Web/JavaScript/Reference/Functions/Arrow_functions を更新 (#480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2021/02/21 時点の英語版に同期 --- .../reference/functions/arrow_functions/index.html | 481 ++++++++++++--------- 1 file changed, 270 insertions(+), 211 deletions(-) (limited to 'files/ja/web/javascript/reference') diff --git a/files/ja/web/javascript/reference/functions/arrow_functions/index.html b/files/ja/web/javascript/reference/functions/arrow_functions/index.html index b178700bd9..b83d1bb7f2 100644 --- a/files/ja/web/javascript/reference/functions/arrow_functions/index.html +++ b/files/ja/web/javascript/reference/functions/arrow_functions/index.html @@ -1,305 +1,366 @@ --- -title: アロー関数 +title: アロー関数式 slug: Web/JavaScript/Reference/Functions/Arrow_functions tags: - - ECMAScript 2015 - - Functions - - Intermediate - - JavaScript - - Reference +- ECMAScript 2015 +- Functions +- Intermediate +- JavaScript +- Language feature +- Reference translation_of: Web/JavaScript/Reference/Functions/Arrow_functions ---
{{jsSidebar("Functions")}}
-

アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this, arguments, super, new.target を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。

+

アロー関数式は、従来の 関数式の簡潔な代替構文ですが、制限があり、すべての場面で使用することができるわけではできません。

-

{{EmbedInteractiveExample("pages/js/functions-arrow.html")}}

+

相違点と制限事項:

-

構文

- -

基本的な構文

+ -
(param1, param2, …, paramN) => { statements }
-(param1, param2, …, paramN) => expression
-// 上記の式は、次の式と同等です: => { return expression; }
+
{{EmbedInteractiveExample("pages/js/functions-arrow.html")}}
-// 引数が 1 つしかない場合、丸括弧 () の使用は任意です: -(singleParam) => { statements } -singleParam => { statements } +

従来の関数とアロー関数の比較

-// 引数がない場合、丸括弧を書かねばいけません: -() => { statements } -
+

「従来の関数」を分解して、最もシンプルな「アロー関数」に段階的に変えていきましょう。
+注: 途中の各ステップは有効な「アロー関数」です。

-

高度な構文

+
// 伝統的な関数
+function (a){
+  return a + 100;
+}
 
-
// object リテラル式を返す場合は、本体を丸括弧 () で囲みます:
-params => ({foo: bar})
+// アロー関数に分解
 
-// 残余引数デフォルト引数 をサポートしています
-(param1, param2, ...rest) => { statements }
-(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
-statements }
+// 1. "function" という語を削除し、引数と本体の開始中括弧の間に矢印を配置する
+(a) => {
+  return a + 100;
+}
 
-// 引数リスト内の分割代入もサポートしています
-var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
-f(); // 6
-
+// 2. 本体の中括弧を削除と "return" という語を削除 -- return は既に含まれています。 +(a) => a + 100; -

説明

+// 3. 引数の括弧を削除 +a => a + 100;
-

hacks.mozilla.org の "ES6 In Depth: Arrow functions" も参照してください。

+
+

上記の通り、 { 中括弧 } と ( 括弧 ) と "return" は省略可能ですが、必要になる場合もあります。

+
-

2 つの理由から、アロー関数が導入されました。1 つ目の理由は関数を短く書きたいということで、2 つ目の理由は this を束縛したくない、ということです。

+

例えば、複数の引数引数なしの場合、引数の周りの括弧を入れなおす必要があります。

-

関数の短縮形

+
// 従来の関数
+function (a, b){
+  return a + b + 100;
+}
 
-
var elements = [
-  'Hydrogen',
-  'Helium',
-  'Lithium',
-  'Beryllium'
-];
+// アロー関数
+(a, b) => a + b + 100;
 
-elements.map(function(element) {
-  return element.length;
-}); // このステートメントが返す配列: [8, 6, 7, 9]
+// 従来の関数 (引数なし)
+let a = 4;
+let b = 2;
+function (){
+  return a + b + 100;
+}
 
-// 上記の通常の関数は、以下のアロー関数として記述できます
-elements.map((element) => {
-  return element.length;
-}); // [8, 6, 7, 9]
+// アロー関数 (引数なし)
+let a = 4;
+let b = 2;
+() => a + b + 100;
-// パラメータが一つしか無い場合、周囲の括弧を削除できます: -elements.map(element => { - return element.length; -}); // [8, 6, 7, 9] +

同様に、本文に処理の追加の行が必要な場合は、中括弧に加えて "return" を入れなおす必要があります (アロー関数は "return" する場所や返値を魔法のように推測できるわけではありません)。

-// アロー関数の唯一のステートメントが `return` の場合、 `return` を削除し -// 周囲の波括弧も削除できます -elements.map(element => element.length); // [8, 6, 7, 9] +
// 従来の関数
+function (a, b){
+  let chuck = 42;
+  return a + b + chuck;
+}
 
-// この場合、必要なのは length property のみなので、分割パラメータを使用できます:
-// 文字列 `"length"` は取得したいプロパティに対応しますが
-// 明らかに特別でない `lengthFooBArX` は、任意の有効な変数名に
-// 変更可能な変数名です
-elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]
+// アロー関数
+(a, b) => {
+  let chuck = 42;
+  return a + b + chuck;
+}
-// この分割パラメータの代入は、以下のように記述することも可能です。ただし、この例では、 -// 作成されたプロパティに `length` の値を代入していないことに注意して下さい。代わりに、 -// 変数 `length` のリテラル名自体が、オブジェクトから取得するプロパティとして使用されます。 -elements.map(({ length }) => length); // [8, 6, 7, 9] -
+

そして最後に、名前付き関数については、変数のようにアロー関数式を扱います。

-

this を束縛しない

+
// 従来の関数
+function bob (a){
+  return a + 100;
+}
 
-

アロー関数以前は、関数の呼び出し方法に応じて自身の this 値を定義していました

+// アロー関数 +let bob = a => a + 100;
- +

構文

-

これは、オブジェクト指向プログラミングをする上で煩わしいということが分かりました。

+

基本的な構文

-
function Person() {
-  // Person() のコンストラクタは、自分のインスタンスを `this` として定義する。
-  this.age = 0;
+

引数が単一の場合。単純な式ならば return は不要です。

- setInterval(function growUp() { - // 非 strict モードでは、growUp() 関数は `this` を - // グローバルオブジェクトとして定義する。 - // (そこで grouUp()が実行されているため) - // Person() コンストラクタが定義した `this` とは違う。 - this.age++; - }, 1000); -} +
param => expression
-var p = new Person();
+

引数が複数の場合は括弧が必要です。単純な式ならば return は不要です。

-

ECMAScript 3/5 では、この問題は this の値をスコープ内の変数に代入することで解決できました。

+
(param1, paramN) => expression
-
function Person() {
-  var that = this;
-  that.age = 0;
+

複数行の文ならば、本体の中括弧と return が必要です。 +

- setInterval(function growUp() { - // このコールバックは、期待されるオブジェクトの値を - // `that` 変数で参照する。 - that.age++; - }, 1000); +
param => {
+  let a = 1;
+  return a + param;
 }
-

あるいは、適切な this の値を対象の関数(上の例では growUp() 関数)に渡すように、束縛関数を作成することもできました。

+

引数が複数の場合は括弧が必要です。複数行の文ならば、本体の中括弧と return が必要です。

-

アロー関数自身は this を持ちません。レキシカルスコープの this 値を使います。つまり、アロー関数内の this 値は通常の変数検索ルールに従います。このためスコープに this 値がない場合、その一つ外側のスコープで this 値を探します。

+
(param1, paramN) => {
+   let a = 1;
+   return a + param1 + paramN;
+}
-

そのため、次のコードで setInterval に渡される関数の this の値は、外部関数の this と同じ値になります:

+

高度な構文

-
function Person(){
-  this.age = 0;
+

オブジェクトリテラル式を返す場合は、式の周りに括弧が必要です。

- setInterval(() => { - this.age++; // |this| は person オブジェクトを適切に参照します。 - }, 1000); -} +
params => ({foo: "a"}) // オブジェクト {foo: "a"} を返す
-var p = new Person();
+

残余引数に対応しています。

-

strict モードとの関連

+
(a, b, ...r) => expression
-

this がレキシカルなもので与えられる場合、strict モードthis に関する規則は無視されます。

+

デフォルト引数に対応しています。

-
var f = () => { 'use strict'; return this; };
-f() === window; // またはグローバルオブジェクト
+
(a=400, b=20, c) => expression
-

他の strict モードの規則は通常通り適用されます。

+

引数の分割代入に対応しています。

-

call や apply からの呼び出し

+
([a, b] = [10, 20]) => a + b;  // result is 30
+({ a, b } = { a: 10, b: 20 }) => a + b; // result is 30
+
-

アロー関数は自身で 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 - }; +
'use strict';
 
-    return f.call(b, a);
+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
 };
 
-console.log(adder.add(1));         // 2 を出力する
-console.log(adder.addThruCall(1)); // やはり 2 を出力する
+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

-

arguments を束縛しない

+

callapplybind の各メソッドは、アロー関数にはふさわしくありません。これらは異なるスコープ内でメソッドを実行できるようにするために設計されているものです。アロー関数は、アロー関数が定義されているスコープに基づいて "this" を確立するからです。

-

アロー関数は自身で arguments オブジェクトを持ちません。そのため、この例では、arguments は囲っているスコープでの同名変数への参照にすぎません。

+

例えば、 callapplybind は、従来の関数ではそれぞれのメソッドにスコープを確立するので、期待通りに動作します。

-
var arguments = [1, 2, 3];
-var arr = () => arguments[0];
+
// ----------------------
+// 従来の関数の例
+// ----------------------
+// 単純化されたオブジェクトで "this" を持つ
+var obj = {
+    num: 100
+}
 
-arr(); // 1
+// "num" を window に設定し、使用されていないことを表す。
+window.num = 2020; // yikes!
 
-function foo(n) {
-  var f = () => arguments[0] + n; // foo は arguments を暗黙的に束縛している。arguments[0] は n である。
-  return f();
+// 単純な従来の関数で "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
 }
 
-foo(3); // 6
+// "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
 
-

多くの場合、残余引数arguments オブジェクトの代わりに使えます。

+

アロー関数を使用する最大の利点は、 DOM レベルのメソッド (setTimeout、setInterval、addEventListener) で、通常、関数が適切なスコープで実行されることを保証するために、クロージャ、call、apply、bind などが必要でした。

-
function foo(n) {
-  var f = (...args) => args[0] + n;
-  return f(10);
+

従来の関数の例:

+ +
var obj = {
+    count : 10,
+    doSomethingLater : function (){
+        setTimeout(function(){ // 関数を window スコープで実行
+            this.count++;
+            console.log(this.count);
+        }, 300);
+    }
 }
 
-foo(1); // 11
+obj.doSomethingLater(); // コンソールに "NaN" と表示。 "count" プロパティは window スコープではないため。
-

メソッドとして使われるアロー関数

+

アロー関数の例:

-

前に述べたように、アロー関数式は非メソッド型の関数に最もよく合っています。これをメソッドとして使った時のことを見てみましょう:

+
var obj = {
+    count : 10,
+    doSomethingLater : function(){ // もちろん、アロー関数はメソッドには向いていない
+        setTimeout( () => { // アロー関数が "obj" 内で作成されるので、それがオブジェクトの "this" と見なされる
+            this.count++;
+            console.log(this.count);
+        }, 300);
+    }
+}
 
-
'use strict';
+obj.doSomethingLater();
-var obj = { - i: 10, - b: () => console.log(this.i, this), - c: function() { - console.log(this.i, this); - } -}; +

arguments のバインドがない

-obj.b(); // prints undefined, Window {...} (or the global object) -obj.c(); // prints 10, Object {...} -
+

アロー関数は自身の arguments オブジェクトを持ちません。そのため、この例では、arguments は囲っているスコープでの同名変数への参照にすぎません。

-

アロー関数は自身の this を持ちません。{{jsxref("Object.defineProperty()")}} を使う例です。

+
var arguments = [1, 2, 3];
+var arr = () => arguments[0];
 
-
'use strict';
-var obj = {
-  a: 10
-};
+arr(); // 1
 
-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'
-  }
-});
-
+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 演算子の使用

-

アロー関数はコンストラクタとして使用できず、new と共に使うとエラーになります。

+

アロー関数はコンストラクターとして使用できず、new と共に使うとエラーが発生します。

-
var Foo = () => {};
+
var Foo = () => {};
 var foo = new Foo(); // TypeError: Foo is not a constructor
-

prototype プロパティの使用

+

prototype プロパティの使用

アロー関数には prototype プロパティはありません。

-
var Foo = () => {};
+
var Foo = () => {};
 console.log(Foo.prototype); // undefined
 
-

yield キーワードの使用

+

yield キーワードの使用

-

{{jsxref("Operators/yield", "yield")}} キーワードはアロー関数内で使用できません(内部で入れ子になった関数が許可されている場合を除く)。結果として、アロー関数はジェネレーターとして使用できません。

+

yield キーワードはアロー関数内で使用できません (内部で入れ子になった関数が許可されている場合を除く)。結果として、アロー関数はジェネレーターとして使用できません。

-

関数の Body 部分

+

関数の本体

アロー関数は、「簡潔文体 (concise body)」か、もしくはより一般的な「ブロック文体 (block body) 」のどちらかを使用することができます。

-

簡潔文体 (concise body) においては、単一の式だけが記述できるので、その式が明示的に return される値となります。しかし、ブロック文体においては、自動的に return はされないので、明示的に return 文を使用する必要があります。

+

簡潔文体 (concise body) においては、単一の式しか記述できないので、その式が暗黙的に return される値となります。しかし、ブロック文体においては、自動的に return はされないので、明示的に return 文を使用する必要があります。

-
var func = x => x * x;
-// 簡潔構文の場合、明示せずとも"return" されます
+
var func = x => x * x;
+// 簡潔構文の場合、暗黙の "return" があります
 var func = (x, y) => { return x + y; };
-// ブロック文体では、明示的に "return" を宣言する必要があります
+// ブロック文体では、明示的な "return" が必要です
 
-

オブジェクトリテラルを返す

+

オブジェクトリテラルを返す

-

短縮構文 params => {object:literal} を使ってオブジェクトリテラルを返そうとしても、期待通りに動作しないことに注意しましょう。

+

簡潔文体 params => {object:literal} を使ってオブジェクトリテラルを返そうとしても、期待通りに動作しないことに注意しましょう。

-
var func = () => { foo: 1 };
+
var func = () => { foo: 1 };
 // 呼び出した func() は undefined を返す!
 
 var func = () => { foo: function() {} };
 // SyntaxError: function 文には名前が必要
 
-

これは、括弧 ({}) 内のコードが文の列として構文解析されてしまっているからです(つまり、foo はオブジェクトリテラル内のキーでなく、ラベルとして扱われています)。

+

これは、括弧 ({}) 内のコードが文の列として構文解析されてしまっているからです (つまり、foo はオブジェクトリテラル内のキーでなく、ラベルとして扱われています)。

オブジェクトリテラルは括弧で囲むのを忘れないでください。

-
var func = () => ({ foo: 1 });
+
var func = () => ({ foo: 1 });
-

改行

+

改行

-

アロー関数には括弧とアロー(矢印)の間に改行を入れられません。

+

アロー関数では、括弧とアロー (矢印) の間に改行を入れることができません。

-
var func = ()
-           => 1;
+
var func = (a, b, c)
+  => 1;
 // SyntaxError: expected expression, got '=>'
-

しかし、下記の例は、アローの後に改行を入れたり、括弧を使って、更に引数の内側で改行を使うことで、綺麗で柔らかなコードに修正できることを確認しています。引数の途中に改行を入れることもできます。

+

しかし、矢印の後に改行を入れたり、以下のように括弧や中括弧を使用して、コードがきれいで滑らかになるように修正することができます。また、引数同士の間にも改行を入れることができます。

-
var func = (a, b, c) =>
+
var func = (a, b, c) =>
   1;
 
 var func = (a, b, c) => (
@@ -316,13 +377,13 @@ var func = (
   c
 ) => 1;
 
-// no SyntaxError thrown
+// SyntaxError は発生しない
-

解析の順序

+

解釈の順序

-

アロー関数内のアロー(矢印)はオペレーターではないですが、アロー関数は通常の関数と異なり、オペレーターを引き継いだ特別な解析ルールを持ちます。

+

アロー関数のアロー (矢印) は演算子ではありませんが、アロー関数には特別な解釈ルールがあり、通常の関数とは演算子の優先順位の扱いが異なります。

-
let callback;
+
let callback;
 
 callback = callback || function() {}; // ok
 
@@ -332,14 +393,16 @@ callback = callback || () => {};
 callback = callback || (() => {});    // ok
 
-

さらなる例

+

-
// 空のアロー関数は undefined を返します
+

基本的な例

+ +
// 空のアロー関数は undefined を返します
 let empty = () => {};
 
 (() => 'foobar')();
 // "foobar" を返します
-// (this is an Immediately Invoked Function Expression)
+// (これは、即時起動型の関数式です。)
 
 var simple = a => a > 15 ? 15 : a;
 simple(16); // 15
@@ -377,29 +440,25 @@ setTimeout( () => {
 }, 1);
 
-

仕様

+

仕様書

- - - - - - - - + + + + + + + +
仕様書
{{SpecName('ESDraft', '#sec-arrow-function-definitions', 'Arrow Function Definitions')}}
仕様書
{{SpecName('ESDraft', '#sec-arrow-function-definitions', 'Arrow Function')}}
-

ブラウザーの実装状況

- -
- +

ブラウザーの互換性

{{Compat("javascript.functions.arrow_functions")}}

-
-

関連項目

+

関連情報

-- cgit v1.2.3-54-g00ecf