This article is in need of a technical review.
This article is in need of an editorial review.
This translation is in progress.
関数は JavaScript の基本的な構成要素のひとつです。また関数は、JavaScript の手続き ― つまり、タスクや値計算を実行する文の集まりです。関数を使うには、呼び出したいスコープ内のどこかでそれを定義する必要があります。
より詳しくは JavaScript の関数に関する完全なリファレンスについての章をご覧ください。
関数を定義する
関数の宣言
関数の定義 (関数の宣言や関数定義文 (function statement) とも呼ばれます) は function
キーワードと、それに続く以下の内容で構成されます。
- 関数の名前。
- 関数への引数のリスト。丸括弧でくくり、コンマで区切ります。
- 関数を定義する JavaScript の文。波括弧
{ }
でくくります。
例えば、次のコードは square
という名前の簡単な関数を定義します:
function square(number) { return number * number; }
関数 square
は number
という名前の引数を 1 つとります。この関数は、引数(すなわち number
)の 2 乗を返すように指示する 1 つの文で構成されています。return
文は、関数が返す値を指定します。
return number * number;
プリミティブなパラメータ(数値など)は値渡しで関数に渡されます。つまり、値は関数に渡されますが、関数がパラメータの値を変更しても、この変更はグローバルな値や関数の呼び出し元の値には影響を与えません。
オブジェクト(すなわち非プリミティブ値、例えば Array
オブジェクトやユーザ定義オブジェクトなど)をパラメータとして渡すと、関数がオブジェクトのプロパティを変更した場合、その変更が関数外でも有効になります。次の例をご覧ください :
function myFunc(theObject) { theObject.make = "Toyota"; } var mycar = {make: "Honda", model: "Accord", year: 1998}; var x, y; x = mycar.make; // x は "Honda" という値になる myFunc(mycar); y = mycar.make; // y は "Toyota" という値になる //(プロパティが関数で変更されている)
注記 : 引数に新たなオブジェクトを代入しても、関数の外部には影響が及びません。これは、オブジェクトのいずれかのプロパティの値ではなく、引数の値を変更しているためです。
function myFunc(theObject) { theObject = {make: "Ford", model: "Focus", year: 2006}; } var mycar = {make: "Honda", model: "Accord", year: 1998}; var x, y; x = mycar.make; // x は "Honda" という値になる myFunc(mycar); y = mycar.make; // y はここでも "Honda" という値になる
関数式
ここまでの関数宣言はすべて構文的な文でしたが、関数は関数式によって作成することもできます。このような関数は無名 (anonymous) にできます。名前をつけなくてもよいのです。例えば、関数 square
は次のように定義できます :
var square = function(number) { return number * number }; var x = square(4) // x の値は 16 となる
ただし関数式は名前をつけることもでき、関数内で自身を参照することや、デバッガのスタックトレースで関数を特定することに利用できます:
var factorial = function fac(n) { return n<2 ? 1 : n*fac(n-1) }; console.log(factorial(3));
関数式は、ある関数を別の関数の引数として渡すときに便利です。次の例では map
関数を定義し、第 1 パラメータとして無名関数を指定して呼び出します:
function map(f,a) { var result = [], // 新しい配列を作成 i; for (i = 0; i != a.length; i++) result[i] = f(a[i]); return result; }
例えば、以下のように呼び出します :
map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);
これは [0, 1, 8, 125, 1000] を返します。
JavaScript では、条件に基づいて関数を定義することもできます。例えば次の関数の定義は、変数 num
が 0 に等しい場合のみ myFunc
という関数を定義します :
var myFunc; if (num == 0){ myFunc = function(theObject) { theObject.make = "Toyota" } }
これまで説明してきた関数の定義に加えて、Function
コンストラクタを、eval()
のように文字列からの関数作成に用いることができます。
メソッドは、オブジェクトのプロパティである関数のことです。オブジェクトとメソッドについて詳しくは、「オブジェクトを利用する」の章をご覧ください。
関数を呼び出す
関数を定義しても、その関数が実行されるわけではありません。関数の定義とは、ただ単に関数に名前をつけ、その関数が呼び出されたときに何をするかを指定することです。関数の呼び出しは、実際に指定したパラメータを用いて指定された動作を実行するということです。例えば、関数 square
を定義した場合、次のようにしてそれを呼び出すことができます:
square(5);
この文は 5 という引数とともに関数を呼び出します。関数は自身の文を実行し、25 という値を返します。
関数は呼び出されるスコープ内になければいけませんが、次の例のように、関数の宣言は呼び出しより後に置くことができます :
console.log(square(5)); /* ... */ function square(n) { return n*n }
関数のスコープは自身が宣言された関数内、あるいはトップレベルで宣言されたのであればプログラム全体になります。
Note: この動作は、上記の構文(すなわち function funcName(){}
)を用いて関数を定義したときに限ることに注意してください。次のコードは動作しません。
console.log(square(5)); square = function (n) { return n * n; }
関数の引数は、文字列や数値に限られてはいません。オブジェクト全体を関数に渡すこともできます。show_props
関数(「オブジェクトを利用する」の章で定義)は、オブジェクトを引数にとる関数の例です。
関数は再帰的にすることもできます。つまり、ある関数がその関数自身を呼び出すこともできるということです。例えば、ここに階乗を計算する関数を示します:
function factorial(n){ if ((n == 0) || (n == 1)) return 1; else return (n * factorial(n - 1)); }
1 から 5 までの階乗の計算は、次のようになります:
var a, b, c, d, e; a = factorial(1); // a の値は 1 となる b = factorial(2); // b の値は 2 となる c = factorial(3); // c の値は 6 となる d = factorial(4); // d の値は 24 となる e = factorial(5); // e の値は 120 となる
関数を呼び出す方法は他にもあります。関数を動的に呼び出す、関数の引数の数を変える、関数を呼び出すコンテキストを実行時に決まる特定のオブジェクトにセットするといった場合があります。関数は関数であるとともにオブジェクトでもあり、また結果としてそれらのオブジェクトはメソッドを持っています(Function
オブジェクトをご覧ください)。そうしたメソッドのひとつ、apply()
メソッドを用いることでこの目的を実現できます。
関数のスコープ
関数の内部で宣言された変数は、関数の外部からアクセスすることができません。これは、変数が関数のスコープ内でのみ定義されているためです。その一方、関数は自身が定義されたスコープ内で定義されているすべての変数や関数にアクセスできます。言い換えると、グローバルスコープで定義された関数は、グローバルスコープで定義されたすべての変数にアクセスできます。ある関数の内部で宣言された関数は、自身の親となる関数内で定義されたすべての変数や、その関数がアクセス権を持つ他の変数にもアクセスできます。
// 以下の変数はグローバルスコープで定義 var num1 = 20, num2 = 3, name = "Chamahk"; // この関数はグローバルスコープで定義 function multiply() { return num1 * num2; } multiply(); // 60 を返す // 入れ子になっている関数の例 function getScore () { var num1 = 2, num2 = 3; function add() { return name + " scored " + (num1 + num2); } return add(); } getScore(); // "Chamahk scored 5" を返す
スコープと関数スタック
再帰
関数は自身を参照し、そして呼び出すことができます。関数が自身を参照する方法は 3 種類あります :
- 関数名
arguments.callee
- 関数を参照したスコープ内変数
例えば、以下のような関数定義を考えてみましょう :
var foo = function bar() { // ここには文が来る };
関数本体の中で、以下のものはすべて同様の意味となります :
bar()
arguments.callee()
foo()
自身を呼び出す関数のことを再帰関数と言います。いくつかの点で、再帰はループに似ています。どちらも同じコードを何度も実行しますし、条件(無限ループを防ぐため、というより無限再帰を防ぐため)が必要です。例えば、以下のループは、:
var x = 0; while (x < 10) { // "x < 10" が終了条件 // 何らかの処理を行う x++; }
再帰関数とその呼び出しとに置き換えることができます :
function loop(x) { if (x >= 10) // "x >= 10" が終了条件 ("!(x < 10)" と同等) return; // 何らかの処理を行う loop(x + 1); // 再帰呼出し } loop(0);
一方で、単純な反復ループでは行えないアルゴリズムもあります。例えば、ツリー構造のすべてのノード(例えば DOM )を取得するのに、再帰を使うとより簡単に行えます :
function walkTree(node) { if (node == null) // return; // ノードに対し処理を行う for (var i = 0; i < node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }
関数 loop
と比較して、それぞれの再帰呼出しによってさらに再帰呼出しが行われています。
どんな再帰アルゴリズムも再帰でないものに書き換えることが可能です、しかしロジックはより複雑になり、データをスタックしておく必要がたびたび出てきます。実際、再帰自体がスタックを使用しています。それが関数スタックです。
以下の例で、スタックとはどういったふるまいをするのか見ることができます :
function foo(i) { if (i < 0) return; console.log('begin:' + i); foo(i - 1); console.log('end:' + i); } foo(3); // Output: // begin:3 // begin:2 // begin:1 // begin:0 // end:0 // end:1 // end:2 // end:3
入れ子の関数とクロージャ
関数の中に関数を入れ子に(ネスト)することができます。入れ子になった(内部の)関数は、それを含んでいる(外部の)関数からはプライベートとなります。これによりクロージャが作られます。クロージャとは、環境に束縛された(式によって「閉じ込められた」)変数を自由に持たせることができる式(通常は一つの関数)のことです。
入れ子になった関数はクロージャなので、これはつまり、入れ子になった関数は内包する関数の引数と変数を「継承」することができるということです。別の言い方をすれば、内部の関数は外部の関数のスコープを持っているということです。
まとめると :
- 内部の関数へは、外部関数内の文からのみアクセスできます。
- 内部の関数はクロージャを形作ります。内部関数は外部関数の引数と変数を利用でき、その一方外部関数は内部関数の引数と変数を利用できません。
以下の実例では入れ子になった関数が示されています :
function addSquares(a,b) { function square(x) { return x * x; } return square(a) + square(b); } a = addSquares(2,3); // 13 を返す b = addSquares(3,4); // 25 を返す c = addSquares(4,5); // 41 を返す
内部の関数はクロージャとなるので、外部の関数からクロージャを呼び出し、外部と内部両方の関数に対し引数を指定することができます :
function outside(x) { function inside(y) { return x + y; } return inside; } fn_inside = outside(3); // このように考えてください : 与えられたものに 3 を加算する関数を代入します result = fn_inside(5); // 8 を返す result1 = outside(3)(5); // 8 を返す
変数の保護
inside
が返されるとき、変数 x
がどのように保護されるのかに注目してください。クロージャはそれ自身が参照しているすべてのスコープ内の引数と変数を保護することになります。それぞれの呼び出しには潜在的に異なる引数が渡されるので、それぞれの外部からの呼び出しに対し新しいクロージャが作られます。返された inside
がもはやアクセスできなくなった時にのみメモリは開放されます。
これはその他のオブジェクトの内部で参照を保持する場合と違いはないのですが、クロージャの場合は直接参照を設定せず、また情報を取得できないので、変数の保護に関してはクロージャのほうがより明白なのです。
多重入れ子関数
関数は多重に入れ子にすることができます、例えば、関数 C を含む関数 B を含む関数 A と言った具合に。ここで関数 B と関数 C はクロージャとなるので、B は A にアクセスでき、C は B にアクセスできます。さらに、C は A にアクセスできる B にアクセスできるので、C は A にもアクセスできます。このようにして、クロージャは多重スコープを導入できます。つまり関数のスコープが再帰的に包含されているのです。これをスコープチェーンと呼びます(なぜ「チェーン」と呼ぶのかは後で説明します)。
次の例を見てみましょう :
function A(x) { function B(y) { function C(z) { console.log(x + y + z); } C(3); } B(2); } A(1); // 6 がログに出力される (1 + 2 + 3)
この例では、関数 C
は関数 B
の引数 y
と関数 A
の引数 x
にアクセスしています。なぜこれが可能かというと :
- 関数
B
は関数A
に含まれたクロージャとなっています、言い換えるとB
はA
の引数と変数にアクセスできます。 - 関数
C
は関数B
に含まれたクロージャとなっています - クロージャ
B
はA
の中にあり、クロージャC
もA
の中にあるので、C
はB
、そしてさらにA
の引数と変数にアクセスできます。言い換えれば、C
はB
、A
の順でスコープがつながっている (chain) のです。
その一方で、逆は成り立ちません。A
は C
にアクセスできません、なぜなら A
は、C
を変数の一つとして持っている B
の引数や変数にはアクセスできないからです。このように, C
は B
からのみプライベートとなっています。
名前衝突
クロージャ中のスコープに同じ名前の 2 つの引数や変数がある場合、名前衝突が生じます。より内部のスコープが優先されるので、最内部にあるスコープが最優先に、一方最も外側のスコープが最も低い優先度となります。 これがスコープチェーンです。チェーンの最初は最内部のスコープ、そして最後は最外部のスコープとなります。次の例を見てみましょう :
function outside() { var x = 10; function inside(x) { return x; } return inside; } result = outside()(20); // 10 ではなく 20 を返す
文 return x
の箇所で、inside
の引数 x
と outside
の変数 x
との間に名前衝突が起きています。ここでのスコープチェーンは、 { inside
, outside
, グローバルオブジェクト } です。 したがって inside
の x
が outside
の x
より優先され、結果 10 (outside
の x
)ではなく、20 (inside
の x
)が返されます。
クロージャ
クロージャは、JavaScript でもっとも強力な機能のひとつです。JavaScript では関数の入れ子が可能であることに加えて、内側の関数が外側の関数内で定義されたすべての変数や関数に、自由にアクセスできます (さらに、外側の関数がアクセスできる、他の変数や関数すべてにもアクセスできます)。しかし、外側の関数は内側の関数内で定義された変数や関数にアクセスできません。これは、内側の関数の変数に対する一種のセキュリティです。また、内側の関数は外側の関数のスコープにアクセスできることから、外側の関数内で定義された変数や関数は、外側の関数の生存期間を越えて残すように内側の関数が管理する場合に、外側の関数自体より長く残る可能性があります。クロージャは、内側の関数がどうにかして外側の関数の外部のスコープを使用可能にするときに作成されます。
var pet = function(name) { // 外側の関数は変数 "name" を定義 var getName = function() { return name; // 内側の関数は外側の関数の変数 "name" にアクセス可能 } return getName; // 内側の関数を返すことで、外側の関数に公開する }, myPet = pet("Vivie"); myPet(); // "Vivie" を返す
上記のコードより複雑なコードにすることもできます。外側の関数の内部にある変数を操作するメソッドを含む、オブジェクトを返すことができます。
var createPet = function(name) { var sex; return { setName: function(newName) { name = newName; }, getName: function() { return name; }, getSex: function() { return sex; }, setSex: function(newSex) { if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) { sex = newSex; } } } } var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("male"); pet.getSex(); // male pet.getName(); // Oliver
上記の例で、外側の関数の変数 name
は内側の関数からアクセスでき、また内側の関数を通さずに内側の変数へアクセスする他の方法はありません。内側の関数の内部変数は、内側の関数の安全な保存領域として振る舞います。それらは内側の関数向けに協働するデータを、"永続的" かつ安全に保持します。関数は変数を割り当てられる必要さえなく、また名前を持つ必要もありません。
var getCode = (function(){ var secureCode = "0]Eal(eh&2"; // 外部の関数が変更できないようにしたいコード return function () { return secureCode; }; })(); getCode(); // シークレットコードを返す
ただし、クロージャを使用する際に注意すべき落とし穴がいくつかあります。取り囲まれている関数で外側のスコープにある変数と同じ名前の変数を定義した場合、外側のスコープにある変数を再び参照する方法がなくなります。
var createPet = function(name) { // 外側の関数で変数 "name" を定義 return { setName: function(name) { // 内側の関数でも変数 "name" を定義 name = name; // ???外側の関数で定義した "name" へどのようにしてアクセスするのか ??? } } }
魔法のような変数 this
は、クロージャ内でとても扱いにくいものです。this
の参照先は、関数が定義された場所ではなく関数が呼び出された場所に対して全面的に依存するため、注意して使用しなければなりません。クロージャに関するすばらしく、かつ精巧な記事がこちらにあります。
arguments オブジェクトの使用
関数の引数は、配列のようなオブジェクトで管理されます。関数内では、次のようにして渡された引数を指すことができます:
arguments[i]
ここで i
は引数の順序を表す数を指します。これは 0 から始まります。関数に渡された第 1 引数は arguments[0]
となります。引数のトータルの数は arguments.length
で表されます。
arguments
オブジェクトを使用すると、宣言時の仮引数の数よりも多くの引数を用いて関数を呼び出すことができます。これは、関数に渡す引数の数が前もってわからない場合に役立ちます。arguments.length
を使用することで、実際に関数に渡された引数の数を特定することができます。また、arguments
オブジェクトを使用することで、各引数を扱うことができます。
例えば、複数の文字列を連結する関数を考えます。この関数の仮引数は、連結するアイテムを区切るのに用いる文字列のみです。この関数は次のように定義されています:
function myConcat(separator) { var result = "", // リストを初期化する i; // 引数について繰り返し for (i = 1; i < arguments.length; i++) { result += arguments[i] + separator; } return result; }
この関数に引数をいくつも渡すことができます。そして、各引数を文字列の "リスト" に連結します:
// "red, orange, blue, " を返す myConcat(", ", "red", "orange", "blue"); // "elephant; giraffe; lion; cheetah; " を返す myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); // "sage. basil. oregano. pepper. parsley. " を返す myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
変数 arguments
は "配列のような変数" であり、配列ではないことに注意してください。これは数値のインデックスと length
プロパティがあることで、配列のようなものになります。しかし、配列操作のメソッドはまったく持っていません。
さらなる情報については、JavaScript リファレンスの Function
オブジェクトをご覧ください。
定義済み関数
JavaScript には、定義済みのトップレベル関数がいくつかあります:
- eval
- isFinite
- isNaN
- parseInt と parseFloat
- Number と String
- encodeURI、decodeURI、encodeURIComponent、decodeURIComponent (すべて Javascript 1.5 以降で使用可能)
以下の章で、これらの関数を説明します。なお全関数の詳細情報については、JavaScript リファレンスをご覧ください。
eval 関数
eval
関数は JavaScript のコード文字列を、特定のオブジェクトを参照することなく評価します。eval
の構文は次のとおりです:
eval(expr);
ここで expr
は、評価される文字列です。
文字列が式を表している場合は、eval
はその式を評価します。また 1 つ以上の JavaScript の文を表している場合は、eval はその式を実行します。eval
のコードのスコープは、呼び出し元コードのスコープと同じです。演算式を評価するために eval
を呼び出さないでください。JavaScript は自動的に演算式を評価します。
isFinite 関数
isFinite
関数は引数を評価し、それが有限数であるかを判断します。isFinite
の構文は次のとおりです:
isFinite(number);
ここで number
は、評価する数値です。
引数が NaN
、正の無限大、または負の無限大である場合、このメソッドは false
を返し、そうでない場合は true
を返します。
次のコードはクライアントの入力をチェックし、それが有限数であるかを判断します。
if(isFinite(ClientInput)){ /* 適当な処理 */ }
isNaN 関数
isNaN
関数は引数を評価し、それが "NaN" (not a number; 非数) であるかを判断します。isNaN
の構文は次のとおりです:
isNaN(testValue);
ここで testValue
は、評価したい値です。
parseFloat
および parseInt
関数は、非数を渡したときに "NaN" を返します。isNaN
は "NaN" が渡された場合は trne を、そうでない場合は false をそれぞれ返します。
次のコードは floatValue
を評価し、それが数値であるかを判断し、その結果に応じてプロシージャを呼び出します:
var floatValue = parseFloat(toFloat); if (isNaN(floatValue)) { notFloat(); } else { isFloat(); }
parseInt 関数および parseFloat 関数
2 つの「パース」関数、parseInt
および parseFloat
は、引数に文字列が与えられたときに数値を返します。
parseFloat
の構文は次のとおりです:
parseFloat(str);
parseFloat
は引数である文字列 str
をパースし、浮動小数点数を返そうとします。符号 (+ または -)、数字 (0-9)、小数点、または指数以外の文字に出くわすと、そこまでの値を返し、その文字および後続の文字すべてを無視します。最初の文字が数値に変換できない場合は、"NaN" (not a number; 非数) を返します。
parseInt
の構文は次のとおりです:
parseInt(str [, radix]);
parseInt
は第 1 引数である文字列 str
をパースし、指定した radix
(基数) の整数を返そうとします。radix
は省略可能な第 2 引数です。例えば、10 という基数では 10 進数に変換し、8 では 8 進数、16 では 16 進数というように変換します。10 より大きな基数では、アルファベットで 9 より大きい数を表します。例えば 16 進数 (基数 16) では、A から F が使用されます。
parseInt
は指定した基数の数値ではない文字に出くわすと、その文字と後続の文字すべてを無視し、それまでにパースした整数の値を返します。最初の文字を指定した基数の値に変換できない場合は、"NaN" を返します。parseInt
は文字列を切り捨てて整数の値にします。
Number 関数および String 関数
Number
および String
関数は、オブジェクトを数値や文字列に変換します。これらの関数の構文は以下のとおりです:
var objRef; objRef = Number(objRef); objRef = String(objRef);
ここで objRef
は、オブジェクト参照を表します。Number 関数は、オブジェクトの valueOf() メソッドを用います。String 関数は、オブジェクトの toString() メソッドを用います。
次の例では、Date
オブジェクトを理解できる文字列に変換します。
var D = new Date(430054663215), x; x = String(D); // x は "Thu Aug 18 04:37:43 GMT-0700 (Pacific Daylight Time) 1983" と等しい
次の例では、String
オブジェクトを Number
オブジェクトに変換します。
var str = "12", num; num = Number(str);
この結果を確認できます。DOM の write()
メソッドと JavaScript の typeof
演算子を使用してください。
var str = "12", num; document.write(typeof str); document.write("<br/>"); num = Number(str); document.write(typeof num);
escape 関数および unescape 関数
escape
および unescape
関数は非 ASCII 文字に対して適切に動作せず、非推奨になりました。JavaScript 1.5 以降では encodeURI
、decodeURI
、encodeURIComponent
、および decodeURIComponent
を使用してください。
escape
および unescape
関数は、文字列のエンコードやデコードを行います。escape
関数は ISO Latin 文字セットで表された、引数の 16 進エンコーディングを返します。unescape
関数は、指定した 16 進エンコーディングの値に対する ASCII 文字列を返します。
これらの関数の構文は以下のとおりです:
escape(string); unescape(string);
これらの関数は、主にサーバサイド JavaScript で URL 中の名前と値のペアのエンコードやデコードに使用されます。