From b33c18547aa98c15568a3faa988a16e409f11d6a Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Sat, 8 May 2021 22:05:39 +0900 Subject: Web/JavaScript/Guide/Functions を更新 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 2021/04/28 時点の英語版に同期 - 原語併記マクロを削除 --- files/ja/web/javascript/guide/functions/index.html | 427 +++++++++++---------- 1 file changed, 216 insertions(+), 211 deletions(-) (limited to 'files/ja/web/javascript/guide') diff --git a/files/ja/web/javascript/guide/functions/index.html b/files/ja/web/javascript/guide/functions/index.html index c519b43ae3..66f79a03f2 100644 --- a/files/ja/web/javascript/guide/functions/index.html +++ b/files/ja/web/javascript/guide/functions/index.html @@ -6,44 +6,44 @@ tags: - Functions - Guide - JavaScript - - 'l10n:priority' + - l10n:priority translation_of: Web/JavaScript/Guide/Functions ---
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}
-

関数は JavaScript の基本的な構成要素のひとつです。また関数は、JavaScript の手続き ― つまり、タスクや値計算を実行する文の集まりです。関数を使うには、呼び出したいスコープ内のどこかでそれを定義する必要があります。

+

関数は、 JavaScript の基本的な構成要素のひとつです。 JavaScript の関数は、プロシージャに似ています。 — タスクを実行したり値を計算したりする一連の文ですが、プロシージャが関数として認められるためには、何らかの入力を受け取り、入力と出力の間に何らかの明白な関係がある出力を返す必要があります。関数を使用するには、呼び出したいスコープのどこかで関数を定義する必要があります。

より詳しくは JavaScript の関数に関する完全なリファレンスについての章をご覧ください。

-

関数を定義する

+

関数の定義

-

関数の宣言

+

関数宣言

-

関数の定義関数の宣言や{{原語併記("関数定義文","function statement")}} とも呼ばれます)は function キーワードと、それに続く以下の内容で構成されます。

+

関数定義 (関数宣言関数定義文 とも呼ばれます) は function キーワードと、それに続く以下の内容で構成されます。

-

例えば、次のコードは square という名前の簡単な関数を定義します :

+

例えば、次のコードは square という名前の簡単な関数を定義します。

-
function square(number) {
+
function square(number) {
   return number * number;
 }
 
-

関数 squarenumber という名前の引数を 1 つとります。この関数は、引数(すなわち number)の 2 乗を返すように指示する 1 つの文で構成されています。return 文は、関数が返す値を指定します。

+

関数 squarenumber という名前の引数を 1 つとります。この関数は、引数 (すなわち number) の 2 乗を返すように指示する 1 つの文で構成されています。return 文は、関数が返す値を指定します。

-
return number * number;
+
return number * number;
 
-

プリミティブなパラメータ(数値など)は値渡しで関数に渡されます。つまり、値は関数に渡されますが、関数がパラメータの値を変更しても、この変更はグローバルな値や関数の呼び出し元の値には影響を与えません

+

プリミティブな引数 (数値など) は値渡しで関数に渡されます。つまり、値は関数に渡されますが、関数が引数の値を変更しても、この変更はグローバルな値や関数の呼び出し元の値には影響を与えません

-

オブジェクト(すなわち非プリミティブ値、例えば {{jsxref("Array")}} オブジェクトやユーザー定義オブジェクトなど)をパラメータとして渡すと、関数がオブジェクトのプロパティを変更した場合、その変更が関数外でも有効になります。次の例をご覧ください :

+

オブジェクト (すなわち非プリミティブ値、例えば {{jsxref("Array")}} オブジェクトやユーザー定義オブジェクトなど) を引数として渡すと、関数がオブジェクトのプロパティを変更した場合、その変更が関数外でも有効になります。次の例をご覧ください。

-
function myFunc(theObject) {
+
function myFunc(theObject) {
   theObject.make = 'Toyota';
 }
 
@@ -54,26 +54,28 @@ x = mycar.make; // x は "Honda" という値になる
 
 myFunc(mycar);
 y = mycar.make; // y は "Toyota" という値になる
-                //(プロパティが関数で変更されている)
+                // (プロパティが関数で変更されている) 
 
-

関数式

+

関数式

-

ここまでの関数宣言はすべて構文的な文でしたが、関数は関数式によって作成することもできます。このような関数は{{原語併記("無名","anonymous")}} にできます。名前をつけなくてもよいのです。例えば、関数 square は次のように定義できます :

+

ここまでの関数宣言はすべて構文的な文でしたが、関数は関数式によって作成することもできます。

-
const square = function(number) { return number * number }
+

このような関数は無名 (anonymous) にすることができます。名前をつけなくてもよいのです。例えば、関数 square は次のように定義することができます。

+ +
const square = function(number) { return number * number }
 var x = square(4) // x の値は 16 となる
-

ただし関数式は名前をつけることもでき、関数内で自身を参照することや、デバッガーのスタックトレースで関数を特定することに利用できます:

+

ただし、関数式には名前を指定することもできます。名前を指定することで、関数が自分自身を参照することができ、また、デバッガーのスタックトレースで関数を特定しやすくなります。

-
const factorial = function fac(n) { return n < 2 ? 1 : n * fac(n - 1) }
+
const factorial = function fac(n) { return n < 2 ? 1 : n * fac(n - 1) }
 
 console.log(factorial(3))
 
-

関数式は、ある関数を別の関数の引数として渡すときに便利です。次の例では map 関数を定義し、第 1 引数に関数を取り、第 2 引数に配列を取ります:

+

関数式は、ある関数を別の関数の引数として渡すときに便利です。次の例では、第 1 引数に関数を、第 2 引数に配列を受け取るべき map 関数を示しています。

-
function map(f, a) {
+
function map(f, a) {
   let result = []; // 新しい配列を作成
   let i; // 変数の宣言
   for (i = 0; i != a.length; i++)
@@ -84,59 +86,61 @@ console.log(factorial(3))
 
 

下記のコードでは、この関数は関数式で定義された関数を受け取って、2 つ目の引数で受け取った配列の各要素に対して実行しています。

-
function map(f, a) {
+
function map(f, a) {
   let result = []; // 新しい配列を作成
   let i; // 変数の宣言
   for (i = 0; i != a.length; i++)
     result[i] = f(a[i]);
   return result;
 }
-var f = function(x) {
+const f = function(x) {
    return x * x * x;
 }
 let numbers = [0, 1, 2, 5, 10];
 let cube = map(f,numbers);
 console.log(cube);
-

これは [0, 1, 8, 125, 1000] を返します。

+

これは [0, 1, 8, 125, 1000] を返します。

-

JavaScript では、条件に基づいて関数を定義することもできます。例えば次の関数の定義は、変数 num が 0 に等しい場合のみ myFunc という関数を定義します :

+

JavaScript では、条件に基づいて関数を定義することもできます。例えば次の関数の定義は、 myFunc という関数を、変数 num0 に等しい場合のみ定義します。

-
var myFunc;
+
var myFunc;
 if (num === 0) {
   myFunc = function(theObject) {
     theObject.make = 'Toyota';
   }
 }
-

これまで説明してきた関数の定義に加えて、{{jsxref("Function")}} コンストラクタを、{{jsxref( "eval", "eval()")}} のように文字列からの関数作成に用いることができます。

+

これまで説明してきた関数の定義に加えて、{{jsxref("Function")}} コンストラクターを、{{jsxref("Global_Objects/eval", "eval()")}} のような文字列からの関数作成に用いることができます。

+ +

メソッドは、オブジェクトのプロパティである関数のことです。オブジェクトとメソッドについて詳しくは、「オブジェクトを利用する」の章をご覧ください。

-

メソッドは、オブジェクトのプロパティである関数のことです。オブジェクトとメソッドについて詳しくは、「オブジェクトを利用する」の章をご覧ください。

+

関数の呼び出し

-

関数を呼び出す

+

関数を定義しても、その関数が実行されるわけではありません。関数の定義とは、ただ単に関数に名前をつけ、その関数が呼び出されたときに何をするかを指定することです。

-

関数を定義しても、その関数が実行されるわけではありません。関数の定義とは、ただ単に関数に名前をつけ、その関数が呼び出されたときに何をするかを指定することです。関数の呼び出しは、実際に指定したパラメータを用いて指定された動作を実行するということです。例えば、関数 square を定義した場合、次のようにしてそれを呼び出すことができます:

+

関数の呼び出しは、指定した引数を用いて実際に指定された動作を実行します。例えば、 square という関数を定義した場合、次のようにして呼び出すことができます。

-
square(5);
+
square(5);
 
-

この文は 5 という引数とともに関数を呼び出します。関数は自身の文を実行し、25 という値を返します。

+

この文は 5 という引数とともに関数を呼び出します。関数は自身の文を実行し、 25 という値を返します。

-

関数は呼び出されるスコープ内になければいけませんが、次の例のように、関数の宣言は呼び出しより後に置くことができます :

+

関数を呼び出すときはスコープ内になければいけませんが、次の例のように、関数の宣言を巻き上げる (呼び出しより後に置く) ことができます。

-
console.log(square(5));
+
console.log(square(5));
 /* ... */
 function square(n) { return n * n }
 
-

関数のスコープは自身が宣言された関数内、あるいはトップレベルで宣言されたのであればプログラム全体になります。

+

関数のスコープは自身が宣言された関数内、あるいは最上位で宣言されたのであればプログラム全体になります。

-

注: この動作は、上記の構文(すなわち function funcName(){})を用いて関数を定義したときに限ることに注意してください。次のコードは動作しません。

+

注: この動作は、上記の構文 (すなわち function funcName(){}) を用いて関数を定義したときに限ることに注意してください。次のコードは動作しません。

これは、関数の巻き上げが関数式ではなく関数宣言でしか機能しないことを意味しています。

-
console.log(square)    // square は初期値が undefined の状態で巻き上げられています。
+
console.log(square)    // square は初期値が undefined の状態で巻き上げられています。
 console.log(square(5)) // Uncaught TypeError: square is not a function
 const square = function(n) {
   return n * n;
@@ -144,11 +148,11 @@ const square = function(n) {
 
-

関数の引数は、文字列や数値に限られてはいません。オブジェクト全体を関数に渡すこともできます。show_props 関数(「オブジェクトを利用する」の章で定義)は、オブジェクトを引数にとる関数の例です。

+

関数の引数は、文字列や数値に限られてはいません。オブジェクト全体を関数に渡すこともできます。show_props 関数 (オブジェクトを利用するの章で定義) は、オブジェクトを引数にとる関数の例です。

-

関数はその関数自身を呼び出すこともできます。例えば、ここに階乗を計算する関数を示します:

+

関数はその関数自身を呼び出すこともできます。例えば、ここに階乗を計算する関数を示します。

-
function factorial(n) {
+
function factorial(n) {
   if ((n === 0) || (n === 1))
     return 1;
   else
@@ -156,9 +160,9 @@ const square = function(n) {
 }
 
-

1 から 5 までの階乗の計算は、次のようになります:

+

1 から 5 までの階乗の計算は、次のようになります。

-
var a, b, c, d, e;
+
var a, b, c, d, e;
 a = factorial(1); // a の値は 1 となる
 b = factorial(2); // b の値は 2 となる
 c = factorial(3); // c の値は 6 となる
@@ -166,20 +170,20 @@ d = factorial(4); // d の値は 24 となる
 e = factorial(5); // e の値は 120 となる
 
-

関数を呼び出す方法は他にもあります。関数を動的に呼び出す、関数の引数の数を変える、関数を呼び出すコンテキストを実行時に決まる特定のオブジェクトにセットするといった場合があります。

+

関数を呼び出す方法は他にもあります。関数を動的に呼び出す必要があったり、関数の引数の数が変化したり、関数呼び出しのコンテキストを実行時に決定された特定のオブジェクトに設定する必要があったりする場合がよくあります。

-

関数は関数であるとともにオブジェクトでもあり、また結果としてそれらのオブジェクトはメソッドを持っています({{jsxref("Function")}} オブジェクトをご覧ください)。そうしたメソッドのひとつ、{{jsxref("Function/apply","apply()")}} メソッドを用いることでこの目的を実現できます。

+

関数はそれ自体がオブジェクトです。 — そして、それらのオブジェクトはメソッドを持っています。 ({{jsxref("Function")}} オブジェクトを参照してください。) そのうちのひとつ、 {{jsxref("Function.apply", "apply()")}} メソッドを使って、この目的を達成することができます。

-

関数のスコープ

+

関数のスコープ

関数の内部で宣言された変数は、関数の外部からアクセスすることができません。これは、変数が関数のスコープ内でのみ定義されているためです。その一方、関数は自身が定義されたスコープ内で定義されているすべての変数や関数にアクセスできます。

言い換えると、グローバルスコープで定義された関数は、グローバルスコープで定義されたすべての変数にアクセスできます。ある関数の内部で宣言された関数は、自身の親となる関数内で定義されたすべての変数や、その関数がアクセス権を持つ他の変数にもアクセスできます。

-
// 以下の変数はグローバルスコープで定義
+
// 以下の変数はグローバルスコープで定義
 var num1 = 20,
     num2 = 3,
-    name = 'Chamahk';
+    name = 'Chamakh';
 
 // この関数はグローバルスコープで定義
 function multiply() {
@@ -189,7 +193,7 @@ function multiply() {
 multiply(); // 60 を返す
 
 // 入れ子になっている関数の例
-function getScore () {
+function getScore() {
   var num1 = 2,
       num2 = 3;
 
@@ -200,59 +204,61 @@ function getScore () {
   return add();
 }
 
-getScore(); // "Chamahk scored 5" を返す
+getScore(); // "Chamakh scored 5" を返す
 
-

スコープと関数スタック

+

スコープと関数スタック

-

再帰

+

再帰

-

関数は自身を参照し、そして呼び出すことができます。関数が自身を参照する方法は 3 種類あります :

+

関数は自身を参照し、呼び出すことができます。関数が自身を参照する方法は 3 種類あります。

    -
  1. 関数名
  2. -
  3. arguments.callee
  4. -
  5. 関数を参照したスコープ内変数
  6. +
  7. 関数名
  8. +
  9. arguments.callee
  10. +
  11. 関数を参照しているスコープ内の変数
-

例えば、以下のような関数定義を考えてみましょう :

+

例えば、以下のような関数定義を考えてみましょう。

-
var foo = function bar() {
+
var foo = function bar() {
    // ここには文が来る
 }
 
-

関数本体の中で、以下のものはすべて同様の意味となります :

+

関数本体の中で、以下のものはすべて同様の意味となります。

    -
  1. bar()
  2. -
  3. arguments.callee()
  4. -
  5. foo()
  6. +
  7. bar()
  8. +
  9. arguments.callee()
  10. +
  11. foo()
-

自身を呼び出す関数のことを再帰関数と言います。いくつかの点で、再帰はループに似ています。どちらも同じコードを何度も実行しますし、条件(無限ループを防ぐため、というより無限再帰を防ぐため)が必要です。例えば、以下のループは、:

+

自身を呼び出す関数のことを再帰関数と言います。いくつかの点で、再帰はループに似ています。どちらも同じコードを何度も実行しますし、 (無限ループを防ぐため、というより無限再帰を防ぐために) 条件が必要です。

+ +

例えば、以下のループは・・・

-
var x = 0;
+
var x = 0;
 while (x < 10) { // "x < 10" がループ条件
    // 何らかの処理を行う
    x++;
 }
 
-

再帰関数とその呼び出しとに置き換えることができます :

+

再帰関数の宣言とその呼び出しとに置き換えることができます。

-
function loop(x) {
-  if (x >= 10) // "x >= 10" が終了条件 ("!(x < 10)" と同等)
+
function loop(x) {
+  if (x >= 10) // "x >= 10" が終了条件  ("!(x < 10)" と同等)
     return;
   // 何らかの処理を行う
-  loop(x + 1); // 再帰呼出し
+  loop(x + 1); // 再帰呼び出し
 }
 loop(0);
 
-

一方で、単純な反復ループでは行えないアルゴリズムもあります。例えば、ツリー構造のすべてのノード(例えば DOM )を取得するのに、再帰を使うとより簡単に行えます :

+

一方で、単純な反復ループでは行えないアルゴリズムもあります。例えば、ツリー構造 (例えば DOM) のすべてのノードを取得する処理は、再帰を使うとより簡単です。

-
function walkTree(node) {
+
function walkTree(node) {
   if (node == null) //
     return;
   // ノードに対し処理を行う
@@ -262,13 +268,13 @@ loop(0);
 }
 
-

関数 loop と比較して、それぞれの再帰呼出しによってさらに多数の再帰呼出しが行われています。

+

関数 loop と比較すると、再帰呼び出しごとにさらに多数の再帰呼び出しを行います。

-

どんな再帰アルゴリズムも再帰でないものに書き換えることが可能です、しかしロジックはより複雑になり、データをスタックしておく必要がたびたび出てきます。実際、再帰自体がスタックを使用しています。それが関数スタックです。

+

どんな再帰アルゴリズムも再帰でないものに書き換えることが可能ですが、ロジックがはるかに複雑になることが多く、そのためにはスタックを使用する必要があります。

-

以下の例で、スタックとはどういったふるまいをするのか見ることができます :

+

実際、再帰自体もスタックを使用しています。関数スタックです。このスタックのような動作は、次の例で見ることができます。

-
function foo(i) {
+
function foo(i) {
   if (i < 0)
     return;
   console.log('begin: ' + i);
@@ -288,24 +294,24 @@ foo(3);
 // end: 2
 // end: 3
-

入れ子の関数とクロージャ

+

入れ子の関数とクロージャ

-

関数の中に関数を入れ子に(ネスト)することができます。入れ子になった(内部の)関数は、それを含んでいる(外部の)関数からはプライベートとなります。

+

関数の中に関数を入れ子に (ネスト) することができます。入れ子になった (内側の) 関数は、それを含んでいる (外側の) 関数の外には非公開となります。

-

これによりクロージャが作られます。クロージャとは、環境に束縛された(式によって「閉じ込められた」)変数を自由に持たせることができる式(通常は一つの関数)のことです。

+

これによりクロージャが作られます。クロージャとは、環境に束縛された (式によって「閉じ込められた」) 変数を自由に持たせることができる式 (通常は一つの関数) のことです。

-

入れ子になった関数はクロージャなので、これはつまり、入れ子になった関数は内包する関数の引数と変数を「継承」することができるということです。別の言い方をすれば、内部の関数は外部の関数のスコープを持っているということです。

+

入れ子になった関数はクロージャなので、これはつまり、入れ子になった関数は内包する関数の引数と変数を「継承」することができるということです。別の言い方をすれば、内側の関数は外側の関数のスコープを持っているということです。

-

まとめると :

+

まとめると、次のようになります。

    -
  • 内部の関数へは、外部関数内の文からのみアクセスできます。
  • -
  • 内部の関数はクロージャを形作ります。内部関数は外部関数の引数と変数を利用でき、その一方外部関数は内部関数の引数と変数を利用できません。
  • +
  • 内側の関数へは、外側の関数の中にある文からのみアクセスできます。
  • +
  • 内側の関数はクロージャを形成します。内側の関数は外側の関数の引数と変数を利用できますが、外側の関数は内側の関数の引数と変数を利用できません。
-

以下の実例では入れ子になった関数が示されています :

+

以下の例では入れ子になった関数が示されています。

-
function addSquares(a, b) {
+
function addSquares(a, b) {
   function square(x) {
     return x * x;
   }
@@ -316,42 +322,42 @@ b = addSquares(3, 4); // 25 を返す
 c = addSquares(4, 5); // 41 を返す
 
-

内部の関数はクロージャとなるので、外部の関数からクロージャを呼び出し、外部と内部両方の関数に対し引数を指定することができます :

+

内側の関数はクロージャとなるので、外側の関数からクロージャを呼び出し、外部と内部両方の関数に対し引数を指定することができます。

-
function outside(x) {
+
function outside(x) {
   function inside(y) {
     return x + y;
   }
   return inside;
 }
-fn_inside = outside(3); // このように考えてください : 与えられたものに 3 を加算する関数を代入します
-
+fn_inside = outside(3); // 渡されたものに 3 を加算する関数を取得する、と
+                        // 考えてください。
 result = fn_inside(5); // 8 を返す
 
 result1 = outside(3)(5); // 8 を返す
 
-

変数の保護

+

変数の保護

-

inside が返されるとき、変数 x がどのように保護されるのかに注目してください。クロージャはそれ自身が参照しているすべてのスコープ内の引数と変数を保護することになります。それぞれの呼び出しには異なる引数が渡される可能性があるので、それぞれの outside の呼び出しに対し新しいクロージャが作られます。返された inside がもはやアクセスできなくなった時にのみメモリーは開放されます。

+

inside が返されるとき、変数 x がどのように保護されるのかに注目してください。クロージャはそれ自身が参照しているすべてのスコープ内の引数と変数を保護することになります。それぞれの呼び出しには異なる引数が渡される可能性があるので、 outside を呼び出すたびに新しいクロージャが作られます。返された inside がもはやアクセスできなくなった時にのみメモリーが解放されます。

これはその他のオブジェクトの内部で参照を保持する場合と違いはないのですが、クロージャの場合は直接参照を設定せず、また情報を取得できないので、明白さは劣ります。

-

多重入れ子関数

+

多重入れ子関数

-

関数は多重に入れ子にすることができます。例えば :

+

関数は下記のように、多重に入れ子にすることができます。

    -
  • 関数 (A) は関数 (B) を含み、関数 (B) は関数 (C) を含んでいます。
  • -
  • ここで、関数 BC はクロージャとなるので、BA にアクセスでき、CB にアクセスできます。
  • -
  • さらに、CA にアクセスできる B にアクセスできるので、CA にもアクセスできます。
  • +
  • 関数 (A) の中に関数 (B) があり、関数 (B) の中に関数 (C) があるとします。
  • +
  • 関数 BC はクロージャとなるので、 BA にアクセスでき、 CB にアクセスできます。
  • +
  • さらに、 CA にアクセス可能な B にアクセスできるので、 CA にもアクセスできます。
-

このようにして、クロージャは多重スコープを導入できます。つまり関数のスコープが再帰的に包含されているのです。これをスコープチェーンと呼びます。(なぜ「チェーン」と呼ぶのかは後で説明します。)

+

このようにして、クロージャは多重スコープを導入できます。つまり関数のスコープが再帰的に包含されているのです。これをスコープチェーンと呼びます。 (なぜ「チェーン」と呼ぶのかは後で説明します。)

-

次の例を見てみましょう :

+

次の例を見てみましょう。

-
function A(x) {
+
function A(x) {
   function B(y) {
     function C(z) {
       console.log(x + y + z);
@@ -365,21 +371,21 @@ A(1); // 6 がログに出力される (1 + 2 + 3)
 
 

この例では、関数 C は関数 B の引数 y と関数 A の引数 x にアクセスしています。

-

なぜこれが可能かというと :

+

なぜこれが可能かというと、

    -
  1. 関数 B は関数 A に含まれたクロージャとなっています、言い換えると BA の引数と変数にアクセスできます。
  2. -
  3. 関数 C は関数 B に含まれたクロージャとなっています。
  4. -
  5. クロージャ BA の中にあり、クロージャ CA の中にあるので、CBそしてさらに A の引数と変数にアクセスできます。言い換えれば、CBA の順でスコープが{{原語併記("つながっている","chain")}} のです。
  6. +
  7. 関数 B は関数 A に含まれたクロージャとなっています (すなわち、 BA の引数と変数にアクセスできます)。
  8. +
  9. 関数 C は関数 B に含まれたクロージャとなっています。
  10. +
  11. クロージャ BA の中にあり、 C のクロージャも A の中にあるので、 CB A両方の引数と変数にアクセスできます。言い換えれば、CBA の順でスコープがつながっている (chain) のです。
-

その一方で、逆は成り立ちません。AC にアクセスできません、なぜなら A は、C を変数の一つとして持っている B の引数や変数にはアクセスできないからです。このように CB からのみプライベートとなっています。

+

その一方で、逆は成り立ちません。 AC にアクセスできません。なぜなら A は、 C を変数の一つとして持っている B の引数や変数にはアクセスできないからです。このように CB の外に対してのみ非公開となっています。

-

名前衝突

+

名前の衝突

-

クロージャ中のスコープに同じ名前の 2 つの引数や変数がある場合、名前衝突が生じます。より内部のスコープが優先されるので、最内部にあるスコープが最優先に、一方最も外側のスコープが最も低い優先度となります。これがスコープチェーンです。チェーンの最初は最内部のスコープ、そして最後は最外部のスコープとなります。次の例を見てみましょう :

+

クロージャ中のスコープに同じ名前の 2 つの引数や変数がある場合、名前の衝突が生じます。より内側のスコープが優先されるので、最も内側にあるスコープが最優先に、最も外側のスコープが最も低い優先度となります。これがスコープチェーンです。チェーンの最初は最も内側のスコープ、そして最後は最も外側のスコープとなります。次の例を見てみましょう。

-
function outside() {
+
function outside() {
   var x = 5;
   function inside(x) {
     return x * 2;
@@ -390,30 +396,30 @@ A(1); // 6 がログに出力される (1 + 2 + 3)
 outside()(10); // 10 ではなく 20 を返す
 
-

return x の箇所で、inside の引数 xoutside の変数 x との間に名前衝突が起きています。ここでのスコープチェーンは、{ inside, outside, グローバルオブジェクト } です。したがって insidexoutsidex より優先され、結果 10 (outsidex)ではなく、20 (insidex)が返されます。

+

return x の箇所で、inside の引数 xoutside の変数 x との間に名前の衝突が発生しています。ここでのスコープチェーンは、{ inside, outside, グローバルオブジェクト } です。したがって insidexoutsidex より優先され、結果 10 (outsidex) ではなく、20 (insidex) が返されます。

-

クロージャ

+

クロージャ

-

クロージャは、JavaScript でもっとも強力な機能のひとつです。JavaScript では関数の入れ子が可能であることに加えて、内側の関数が外側の関数内で定義されたすべての変数や関数に対し(外側の関数がアクセスできる、他の変数や関数すべてにも)自由にアクセスできます。

+

クロージャは、 JavaScript でもっとも強力な機能のひとつです。 JavaScript では関数の入れ子が可能であることに加えて、内側の関数が外側の関数内で定義されたすべての変数や関数に対し (外側の関数がアクセスできる、他の変数や関数すべてにも) 自由にアクセスできます。

しかし、外側の関数は内側の関数内で定義された変数や関数にアクセスできません。これは、内側の関数の変数に対する一種のセキュリティ機構を提供します。

また、内側の関数は外側の関数のスコープにアクセスできることから、もし内側の関数が外側の関数よりも長く生存できた場合、外側の関数内で定義された変数や関数は外側の関数よりも長く残る可能性があります。クロージャは、内側の関数が何かしらの形で外側の関数のスコープ外のどこかで使用可能になった場合に作られます。

-
var pet = function(name) {      // 外側の関数は変数 "name" を定義
+
var pet = function(name) {   // 外側の関数は変数 "name" を定義
   var getName = function() {
-    return name;                // 内側の関数は外側の関数の変数 "name" にアクセス可能
+    return name;             // 内側の関数は外側の関数の変数 "name" にアクセス可能
   }
-  return getName;               // 内側の関数を返すことで、外側の関数に公開する
+  return getName;            // 内側の関数を返すことで、外側の関数に公開する
 }
 myPet = pet('Vivie');
 
-myPet();                        // "Vivie" を返す
+myPet();                     // "Vivie" を返す
 

上記のコードより複雑なコードにすることもできます。外側の関数の内部にある変数を操作するメソッドを含む、オブジェクトを返すことができます。

-
var createPet = function(name) {
+
var createPet = function(name) {
   var sex;
 
   return {
@@ -449,8 +455,8 @@ pet.getName();                  // Oliver
 
 

上記の例で、外側の関数の変数 name は内側の関数からアクセスでき、また内側の関数を通さずに内側の変数へアクセスする他の方法はありません。内側の関数の内部変数は、内側の関数の安全な保存領域として振る舞います。それらは内側の関数と連動するデータを、「永続的」かつ「安全に」保持します。関数は変数を割り当てる必要さえなく、また名前を持つ必要もありません。

-
var getCode = (function(){
-  var apiCode = '0]Eal(eh&2';    // 外部の関数が変更できないようにしたいコード
+
var getCode = (function() {
+  var apiCode = '0]Eal(eh&2';    // 外側の関数が変更できないようにしたいコード
 
   return function() {
     return apiCode;
@@ -460,12 +466,12 @@ pet.getName();                  // Oliver
 getCode();    // シークレットコードを返す
 
-
-

警告: クロージャーを使用する際に注意すべき落とし穴がいくつかあります。

+
+

注: クロージャを使用する際に注意すべき落とし穴がいくつかあります。

-取り囲まれている関数で外部スコープの変数と同じ名前の変数を定義した場合、外部スコープにある変数を再び参照する方法がなくなります。(プログラムが内部スコープを終了するまで、内部スコープ変数は外部変数を「上書き」します。) +

取り囲まれている関数で外部スコープの変数と同じ名前の変数を定義した場合、外部スコープにある変数を再び参照する方法がなくなります。 (プログラムが内部スコープを終了するまで、内部スコープ変数は外部変数を「上書き」します。)

-
var createPet = function(name) {  // 外側の関数で "name" という変数を定義します。
+
var createPet = function(name) {  // 外側の関数で "name" という変数を定義します。
   return {
     setName: function(name) {    // 内側の関数も "name" という変数を定義します
       name = name;               // 外側の関数で定義した "name" へどのようにしてアクセスするのか?
@@ -475,21 +481,21 @@ getCode();    // シークレットコードを返す
 
-

arguments オブジェクトの使用

+

arguments オブジェクトの使用

-

関数の{{原語併記("引数", "argument")}}は、配列状オブジェクトで管理されます。関数内では、次のようにして渡された引数を指定することができます :

+

関数の引数 (argument) は、配列風オブジェクトで管理されます。関数内では、次のようにして渡された引数を指定することができます。

-
arguments[i]
+
arguments[i]
 
-

ここで i は引数の順序を表す数で、0 から始まります。関数に渡された第 1 引数は arguments[0] となります。引数のトータルの数は arguments.length で表されます。

+

ここで i は引数の順序を表す数で、 0 から始まります。関数に渡された第 1 引数は arguments[0] となります。引数の総数は arguments.length で表されます。

-

arguments オブジェクトを使用すると、宣言時の引数の数よりも多くの引数を用いて関数を呼び出すことができます。これによって関数に渡す引数の数が前もってわからない場合にしばしば役立ちます。arguments.length を使用することで、実際に関数に渡された引数の数を特定することができます。そして、arguments オブジェクトを使用して各引数にアクセスできます。

+

arguments オブジェクトを使用すると、宣言時の引数の数よりも多くの引数を用いて関数を呼び出すことができます。これによって関数に渡す引数の数が前もってわからない場合にしばしば役立ちます。 arguments.length を使用することで、実際に関数に渡された引数の数を特定することができます。そして、 arguments オブジェクトを使用して各引数にアクセスできます。

-

例えば、複数の文字列を連結する関数を考えます。この関数の引数は、連結するアイテムを区切るのに用いる文字列のみです。この関数は次のように定義されています :

+

例えば、複数の文字列を連結する関数を考えてみましょう。この関数の唯一の正式な引数は,連結する項目を区切る文字を指定する文字列です。この関数は次のように定義されています。

-
function myConcat(separator) {
-   var result = '', // リストを初期化する
+
function myConcat(separator) {
+   var result = ''; // リストを初期化する
    var i;
    // 引数について繰り返し
    for (i = 1; i < arguments.length; i++) {
@@ -499,9 +505,9 @@ getCode();    // シークレットコードを返す
 }
 
-

この関数に引数をいくつも渡すことができます。そして、各引数を文字列の "リスト" に連結します:

+

この関数に引数をいくつも渡すことができます。そして、各引数を文字列の「リスト」に連結します。

-
// "red, orange, blue, " を返す
+
// "red, orange, blue, " を返す
 myConcat(', ', 'red', 'orange', 'blue');
 
 // "elephant; giraffe; lion; cheetah; " を返す
@@ -512,26 +518,26 @@ myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');
 
-

注記 : 変数 arguments は "配列状の変数" であり、配列ではありません。これは数値のインデックスと length プロパティがあることで、配列状となってはいます。しかし、配列操作のメソッドのすべては持っていません。

+

注: 変数 arguments は「配列風の変数」であり、配列ではありません。添字が数値であることと length プロパティがあることで、配列風となってはいます。しかし、配列操作のメソッドのすべてを持っているわけではありません。

さらなる情報については、JavaScript リファレンスの {{jsxref("Function")}} オブジェクトをご覧ください。

-

関数の引数

+

関数の引数

ECMAScript 2015 から、新しい形の引数が 2 つあります。それがデフォルト引数残余引数です。

-

デフォルト引数

+

デフォルト引数

-

JavaScript では、関数の引数はデフォルトで undefined となります。しかし、別のデフォルト値が設定されていれば便利だという状況もあるでしょう。デフォルト引数がここで役に立ちます。

+

JavaScript では、関数の引数は既定で undefined となります。しかし、別の既定値が設定されていれば便利だという状況もあるでしょう。デフォルト引数がここで役に立ちます。

-

デフォルト引数なし(ECMAScript 2015 以前)

+

デフォルト引数がない場合 (ECMAScript 2015 以前)

-

以前、デフォルト値を設定する一般的な方法は、関数の本体で引数の値をテストし、undefined だった場合にある値を割り当てる、というものでした。

+

以前、既定値を設定する一般的な方法は、関数の本体で引数の値をテストし、undefined だった場合にある値を割り当てる、というものでした。

-

以下の例では、呼び出しの際に b に値が割り当てられない場合、a*b の評価の際に b の値は undefined となるため、multiply を呼び出すと NaN が返されます。しかしながら、例の 2 行目でこの問題を回避しています :

+

以下の例では、 b の値が与えられなかった場合、 a*b を評価する際にこの値は undefined となるため、 multiply を呼び出すと NaN が返されます。しかしながら、この例の 2 行目でこの問題を回避しています。

-
function multiply(a, b) {
+
function multiply(a, b) {
   b = typeof b !== 'undefined' ?  b : 1;
 
   return a * b;
@@ -540,43 +546,42 @@ myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');
 multiply(5); // 5
 
-

デフォルト引数あり(ECMAScript 2015 以降)

+

デフォルト引数がある場合 (ECMAScript 2015 以降)

-

デフォルト引数を使えば、関数本体での引数チェックはもう必要ありません。これからは、関数の最初で単純に b1 を代入することができます :

+

デフォルト引数を使えば、関数本体での引数チェックはもう必要ありません。これからは、関数の最初で単純に b1 を代入することができます。

-
function multiply(a, b = 1) {
+
function multiply(a, b = 1) {
   return a * b;
 }
 
 multiply(5); // 5
-

詳細については、リファレンスの「デフォルト引数」をご覧ください。

+

詳細については、リファレンスのデフォルト引数をご覧ください。

-

残余引数

+

残余引数

残余引数の構文によって、不特定多数の引数を配列のように表すことができます。

+

次の例では、2 つ目から最後までの引数をまとめるのに残余引数を使っています。そして最初の引数を使って乗算します。

-

次の例では、2 つ目から最後までの引数をまとめるのに残余引数を使っています。そして最初の引数を使って乗算します。(ここでは次の章で紹介するアロー関数を使っています。)

- -
function multiply(multiplier, ...theArgs) {
+
function multiply(multiplier, ...theArgs) {
   return theArgs.map(x => multiplier * x);
 }
 
 var arr = multiply(2, 1, 2, 3);
 console.log(arr); // [2, 4, 6]
-

アロー関数

+

アロー関数

-

アロー関数式(以前は、そして現在は正しくないがファットアロー関数としても知られる)は関数式と比較してより短い構文を持ち、thisargumentssupernew.target の値を持ちません。アロー関数は常に無名関数です。hacks.mozilla.org によるこのブログ記事もご覧ください : "ES6 In Depth: Arrow functions"

+

アロー関数式 (以前、そして今も誤ってファットアロー関数とも呼ばれる) は関数式と比較してより短い構文を持ち、thisargumentssupernew.target の値を持ちません。アロー関数は常に無名関数です。 hacks.mozilla.org によるブログ記事、 "ES6 In Depth: Arrow functions" も参照してください。

-

アロー関数の導入には 2 つの要素が絡んでいます。それは短縮形の関数this束縛しないことです。

+

アロー関数の導入には 2 つの要素が絡んでいます。それは短縮形の関数this との結びつけがないことです。

-

短縮形の関数

+

短縮形の関数

-

関数パターンによっては、短縮形の関数がうってつけです。比較してみましょう :

+

関数パターンによっては、短縮形の関数がうってつけです。比較してみましょう。

-
var a = [
+
var a = [
   'Hydrogen',
   'Helium',
   'Lithium',
@@ -592,17 +597,17 @@ var a3 = a.map(s => s.length);
 console.log(a3); // logs [8, 6, 7, 9]
 
-

別々の this はない

+

独自の this を持たない

-

アロー関数の導入以前は、すべての新しい関数には自身の this 値が定義されています(コンストラクターの場合は新しいオブジェクトに、strict モード の関数呼び出しの場合は undefined に、関数が「オブジェクトのメソッド」として呼び出された場合はその基底オブジェクトに、といったように)。これはオブジェクト指向プログラミングにとっては厄介です。

+

アロー関数の導入以前は、すべての新しい関数には独自の this 値が定義されていした (コンストラクターの場合は新しいオブジェクトに、 strict モード の関数呼び出しの場合は undefined に、関数が「オブジェクトのメソッド」として呼び出された場合はその基底オブジェクトに、といったように) 。これはオブジェクト指向プログラミングにとっては厄介です。

-
function Person() {
-  // この Person() コンストラクタは自身を `this` と定義します。
+
function Person() {
+  // この Person() コンストラクターは自身を `this` と定義します。
   this.age = 0;
 
   setInterval(function growUp() {
-    // Strict モードでない場合、この growUp() 関数は
-    // Person() コンストラクタによる定義とは異なり、
+    // strict モードでない場合、この growUp() 関数は
+    // Person() コンストラクターによる定義とは異なり、
     // グローバルオブジェクトを `this` として定義します。
     this.age++;
   }, 1000);
@@ -612,23 +617,23 @@ var p = new Person();

ECMAScript 3/5 では、this の値をアクセス可能な別の値に割り当てることでこの問題を解決します。

-
function Person() {
+
function Person() {
   var self = this; // `self` の代わりに `that` を選ぶ人もいます。
                    // どちらか一方を選び、そちらだけを使うようにしましょう。
   self.age = 0;
 
   setInterval(function growUp() {
     // このコールバックは、その値が期待通りのオブジェクトを指す
-    // 変数 `self` を参照している。
+    // 変数 `self` を参照しています。
     self.age++;
   }, 1000);
 }
-

代わりに、束縛関数を使って変数を束縛すれば growUp() 関数に適切な this を渡すことができます。

+

代わりに、束縛関数を使って変数を束縛すれば growUp() 関数に適切な this を渡すことができます。

-

アロー関数は自身の this を持ちません、つまり関数を取り囲む実行コンテキストの this の値が使われます。このため、下記のコードでは、setInterval に渡される関数内の this は、それを取り囲む関数の this と同じ値を持ちます:

+

アロー関数は自身の this を持ちません、つまり関数を取り囲む実行コンテキストの this の値が使われます。このため、下記のコードでは、setInterval に渡される関数内の this は、それを取り囲む関数の this と同じ値を持ちます。

-
function Person(){
+
function Person() {
   this.age = 0;
 
   setInterval(() => {
@@ -638,59 +643,59 @@ var p = new Person();
var p = new Person();
-

定義済み関数

+

定義済み関数

-

JavaScript には、定義済みのトップレベル関数が数種類あります :

+

JavaScript には、定義済みの最上位関数がいくつかあります。

-
{{jsxref("Global_Objects/eval", "eval()")}}
-
-

eval() メソッドは文字列として書き表された JavaScript のコードを評価します。

-
-
{{jsxref("Global_Objects/uneval", "uneval()")}}
-
-

uneval() メソッドは{{jsxref("Object","オブジェクト","","true")}}のソースコードを表す文字列を生成します。

-
-
{{jsxref("Global_Objects/isFinite", "isFinite()")}}
-
-

このグローバル関数 isFinite() は渡された値が有限数であるかを判定します。必要であれば、引数は初めに数へと変換されます。

-
-
{{jsxref("Global_Objects/isNaN", "isNaN()")}}
-
-

isNaN() 関数は値が {{jsxref("Global_Objects/NaN", "NaN")}}(非数)であるかどうかを判定します。注記 : isNaN 関数内での強制型変換は変わったルールを持っています。値が非数であるかを判定するには、代わりに ECMAScript 2015 で定義された {{jsxref("Number.isNaN()")}} か、typeof を使うことができます。

-
-
{{jsxref("Global_Objects/parseFloat", "parseFloat()")}}
-
-

parseFloat() 関数は引数の文字列をパースして浮動小数点数を返します。

-
-
{{jsxref("Global_Objects/parseInt", "parseInt()")}}
-
-

parseInt() 関数は引数の文字列をパースして指定された基数(数学的記数法における基数)による整数を返します。

-
-
{{jsxref("Global_Objects/decodeURI", "decodeURI()")}}
-
-

decodeURI() 関数は前もって {{jsxref("Global_Objects/encodeURI", "encodeURI")}} 関数によって、あるいは同様の方法で作られた{原語併記("統一資源識別子","Uniform Resource Identifier, URI"}} をデコードします。

-
-
{{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent()")}}
-
-

decodeURIComponent() メソッドは前もって {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} によって、あるいは同様の方法で作られた統一資源識別子 (URI) をデコードします。

-
-
{{jsxref("Global_Objects/encodeURI", "encodeURI()")}}
-
-

encodeURI() メソッドは特定の文字をそれぞれ UTF-8 文字エンコーディングで表された 1 文字から 4 文字のエスケープシーケンス(サロゲートペア文字からなる文字にのみ 4 文字のエスケープシーケンスが必要)に置き換えることで統一資源識別子 (URI) をエンコードします。

-
-
{{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent()")}}
-
-

encodeURIComponent() メソッドは特定の文字をそれぞれ UTF-8 文字エンコーディングで表された 1 文字から 4 文字のエスケープシーケンス(サロゲートペア文字からなる文字にのみ 4 文字のエスケープシーケンスが必要)に置き換えることで統一資源識別子 (URI) コンポーネントをエンコードします。

-
-
{{jsxref("Global_Objects/escape", "escape()")}}
-
-

廃止予定の escape() メソッドはある文字列を 16 進数によるエスケープシーケンスで置換した新しい文字列を計算します。代わりに {{jsxref("Global_Objects/encodeURI", "encodeURI")}} か {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} を使用してください。

-
-
{{jsxref("Global_Objects/unescape", "unescape()")}}
-
-

廃止予定の unescape() メソッドはある文字列中の 16 進数によるエスケープシーケンスを、それが表す所定の文字に置換した新しい文字列を計算します。エスケープシーケンスは {{jsxref("Global_Objects/escape", "escape")}} といった関数によって提供されているかもしれません。unescape() は廃止予定なので、代わりに {{jsxref("Global_Objects/decodeURI", "decodeURI()")}} か {{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent")}} を使用してください。

-
+
{{jsxref("Global_Objects/eval", "eval()")}}
+
+

eval() メソッドは文字列として書き表された JavaScript のコードを評価します。

+
+
{{jsxref("Global_Objects/uneval", "uneval()")}}
+
+

uneval() メソッドは{{jsxref("Object","オブジェクト","","true")}}のソースコードを表す文字列を生成します。

+
+
{{jsxref("Global_Objects/isFinite", "isFinite()")}}
+
+

このグローバル関数 isFinite() は渡された値が有限数であるかを判定します。必要であれば、引数は初めに数値へと変換されます。

+
+
{{jsxref("Global_Objects/isNaN", "isNaN()")}}
+
+

isNaN() 関数は値が {{jsxref("Global_Objects/NaN", "NaN")}} (非数) であるかどうかを判定します。注: isNaN 関数内での強制型変換は変わったルールを持っています。値が非数であるかを判定するには、代わりに ECMAScript 2015 で定義された {{jsxref("Number.isNaN()")}} か、 typeof を使うことができます。

+
+
{{jsxref("Global_Objects/parseFloat", "parseFloat()")}}
+
+

parseFloat() 関数は引数の文字列を解釈して浮動小数点数を返します。

+
+
{{jsxref("Global_Objects/parseInt", "parseInt()")}}
+
+

parseInt() 関数は引数の文字列を解釈して指定された基数 (数学的記数法における基数) による整数を返します。

+
+
{{jsxref("Global_Objects/decodeURI", "decodeURI()")}}
+
+

decodeURI() 関数は前もって {{jsxref("Global_Objects/encodeURI", "encodeURI")}} 関数によって、または同様の方法で作られた URL (Uniform Resource Identifier) をデコードします。

+
+
{{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent()")}}
+
+

decodeURIComponent() メソッドは前もって {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} 関数によって、あるいは同様の方法で作られた部分的な URI をデコードします。

+
+
{{jsxref("Global_Objects/encodeURI", "encodeURI()")}}
+
+

encodeURI() メソッドは、特定の文字をそれぞれ UTF-8 文字エンコーディングで表された 1 から 4 つのエスケープシーケンス (4 つのエスケープシーケンスはサロゲートペア文字のみ) に置き換えることで URI をエンコードします。

+
+
{{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent()")}}
+
+

encodeURIComponent() メソッドは、特定の文字をそれぞれ UTF-8 文字エンコーディングで表された 1 から 4 つのエスケープシーケンス (4 つのエスケープシーケンスはサロゲートペア文字のみ) に置き換えることで部分的な URI をエンコードします。

+
+
{{jsxref("Global_Objects/escape", "escape()")}}
+
+

非推奨の escape() メソッドはある文字列を 16 進数によるエスケープシーケンスで置換した新しい文字列を計算します。代わりに {{jsxref("Global_Objects/encodeURI", "encodeURI")}} または {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} を使用してください。

+
+
{{jsxref("Global_Objects/unescape", "unescape()")}}
+
+

非推奨の unescape() メソッドはある文字列中の 16 進数によるエスケープシーケンスを、それが表す所定の文字に置換した新しい文字列を計算します。エスケープシーケンスは {{jsxref("Global_Objects/escape", "escape")}} といった関数によって提供されているかもしれません。 unescape() は非推奨なので、代わりに {{jsxref("Global_Objects/decodeURI", "decodeURI()")}} または {{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent")}} を使用してください。

+

{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}

-- cgit v1.2.3-54-g00ecf