ECMAScript 闭包(closure)

ECMAScriptの関数は実際には機能完全なオブジェクトです。

Function オブジェクト(クラス)

ECMAScriptで最も興味深いのは、関数が実際には機能完全なオブジェクトであることかもしれません。

Function クラスは開発者が定義するどんな関数も表現できます。

Function クラスを使用して直接関数を作成する構文は以下の通りです:

var function_name = new function()arg1, arg2, ... , argN, function_body)

上記の形式では、それぞれの arg すべて同じ引数であり、最後の引数が関数本体(実行するコード)です。これらの引数は文字列でなければなりません。

この関数を覚えていますか?

function sayHi(sName, sMessage) {
  alert('Hello ' + sName + sMessage);
}

以下のように定義することもできます:

var sayHi 
= 
new Function("sName", "sMessage", "alert('Hello ' + sName + sMessage)");

文字列の関係で、この形式で書くのは少し難しいですが、関数が参照型であることや、Function クラスで明示的に作成された関数と同じ動作をすることを理解するのに役立ちます。

以下の例を見てください:

function doAdd(iNum) {
  alert(iNum + 20);
}
function doAdd(iNum) {
  alert(iNum + 10);
}
doAdd(10);	//「20」として出力

ご存知の通り、2番目の関数は1番目の関数をオーバーライドし、doAdd(10) は「30」ではなく「20」と出力します。

以下の形式でコードを書き換えた場合、この概念が明確になります:

var doAdd = new Function("iNum", "alert(iNum + 20)");
var doAdd = new Function("iNum", "alert(iNum + 10)");
doAdd(10);

このコードを観察してください、明らかに doAdd の値が異なるオブジェクトのポインタに変更されています。関数名は関数オブジェクトへの参照値であり、他のオブジェクトと同じように動作します。2つの変数が同じ関数を指すようにすることもできます:

var doAdd = new Function("iNum", "alert(iNum + 10)");
var alsodoAdd = doAdd;
doAdd(10);	//「20」として出力
alsodoAdd(10);	//「20」として出力

ここでは、変数 doAdd が関数として定義され、alsodoAdd が同じ関数を指すポインタとして宣言されます。この2つの変数を使うと、関数のコードを実行し、同じ結果「20」を出力できます。したがって、関数名が単なる関数のポインタである場合、関数を他の関数に引数として渡すことはできますか?答えは肯定的です!

function callAnotherFunc(fnFunction, vArgument) {
  fnFunction(vArgument);
}
var doAdd = new Function("iNum", "alert(iNum + 10)");
callAnotherFunc(doAdd, 10);	//「20」を出力

上記の例では、callAnotherFunc() には 2 つの引数があります - 呼び出す関数とその関数に渡す引数。このコードは doAdd() を callAnotherFunc() 関数に渡し、引数として 10 を渡し、「20」を出力します。

注意:Function 构造関数を使用して関数を作成することはできますが、それを使用することはお勧めしません。なぜなら、それを使用して関数を定義するのは、伝統的な方法よりもはるかに遅いからです。ただし、すべての関数は Function クラスのインスタンスと見なされるべきです。

Function オブジェクトの length 属性

先ほど述べたように、関数は参照型であり、したがって属性やメソッドも持っています。

ECMAScript が定義する属性 length は、関数が期待する引数の数を示しています。例えば:

function doAdd(iNum) {
  alert(iNum + 10);
}
function sayHi() {
  alert("Hi");
}
alert(doAdd.length);	//「1」を出力
alert(sayHi.length);	//「0」を出力

関数 doAdd() は 1 つの引数を定義していますので、その length は 1 です;sayHi() は引数を定義していないので、length は 0 です。

覚えておいてください、定義された引数の数に関わらず、ECMAScript は任意の数の引数を受け入れることができます(最大 25 までです)。これは「関数の概要」の章で説明されています。属性 length は、デフォルトで期待される引数の数を確認するための簡単な方法を提供するだけです。

Function オブジェクトのメソッド

Function オブジェクトも、すべてのオブジェクトと共有する valueOf() メソッドと toString() メソッドを持っています。これらのメソッドはすべての関数のソースコードを返し、デバッグ時に特に役立ちます。例えば:

function doAdd(iNum) {
  alert(iNum + 10);
}
document.write(doAdd.toString());

上記のコードは doAdd() 関数のテキストを出力します。実際に試してみる