Function Expression Statements ― 2005年12月10日 21時32分
もじら組フォーラムで書いたことを再掲。以下 JavaScript といった場合 JScript などは含まないものとする。
JavaScript の function
ECMAScript 3 で function キーワードが使われる構文には FunctionDeclaration と FunctionExpression があり、以下のように定義されている。
FunctionDeclarationfunctionIdentifier(FormalParameterListopt){FunctionBody}FunctionExpressionfunctionIdentifieropt(FormalParameterListopt){FunctionBody}FunctionBodySourceElementsProgramSourceElementsSourceElementsSourceElementSourceElementsSourceElementSourceElementStatementFunctionDeclaration
これをみればわかるとおり、FunctionDeclaration はプログラム直下か関数本体の直下にしか置けない。下の例でいうと (1) は問題ないが (2) は ECMAScript としては正しくないということだ。(2) の場合 g は FunctionExpression とみなされるのではと思うかもしれないが、ExpressionStatement を function キーワードで開始することはできないのでそれも成り立たない。
// (1)
function f() {
function g() {}
}
// (2)
function f() {
do {
function g() {}
} while (false);
}
しかし、JavaScript ではバージョン 1.5 から Function Expression Statements という機能が追加され、通常の文が置ける場所ならどこでも関数を定義できるようになった (すなわち JavaScript では (2) も正しいコードである) 。ここではこれを仮に FunctionStatement と呼ぶことにする。
FunctionStatementfunctionIdentifieropt(FormalParameterListopt){FunctionBody}
FunctionDeclaration と FunctionStatement の最大の違いは処理されるタイミングである。FunctionDeclaration はコンパイル時に処理されるその宣言が含まれる関数 (宣言がトップレベルにある場合はプログラム全体) の実行に先立って処理されるが FunctionStatement は実行時に処理される。また、FunctionExpression との違いとして、FunctionExpression の Identifier は FunctionBody 内でのみ有効だが、FunctionStatement では文の評価後にその Identifier を使って関数を参照できるようになることが挙げられる。
以上の特徴から、if 文を使った関数定義の振り分けなどもできる。
// (3)
var condition = true;
if (condition) {
function f() {
return true;
}
} else {
function f() {
return false;
}
}
alert(f()); // JavaScript (Firefox など) では true
また、FunctionStatement が実行時に処理されるということは、その文の評価前はその関数を参照できないということでもある。以下のコードは JavaScript では例外が投げられる。
// (4)
try {
f();
function f() {
alert("Succeed!");
}
} catch (e) {
alert(e); // ReferenceError: f is not defined
}
なお、FunctionStatement に Identifier が存在する場合、FunctionStatement の文としての返り値は空値 (empty) になるが、Identifier が存在しない場合はその関数オブジェクトが返る。ただし Firefox 1.0 では Identifier を省略できない (省略するとシンタックスエラーになる) 。
JScript の function
だが、Function Expression Statements はあくまでも JavaScript だけの機能であり、JScript などでは実装されていない。しかし、JScript や Opera の ECMAScript 実装でも任意の場所に function 文を置くことができる。ただし、それが処理されるのはコンパイル時であり、JavaScript のような動的な関数定義はできない。それが証拠に IE や Opera で上記 (3) のコードを実行してみると false と表示される。
さらに、JScript の場合は、FunctionExpression さえもコンパイル時に処理し、その Identifier を変数オブジェクトに登録してしまう。結果以下のコードで "Hello" と表示されてしまうのである。これは明らかに ECMAScript に違反している。
// (5)
alert(f()); // "Hello"
var g = function f() { return "Hello"; };
alert(f == g); // false
コメント
_ Garrett Smith ― 2010年05月15日 02時31分
_ nanto_vi ― 2010年10月02日 13時55分
コメントをどうぞ
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※投稿には管理者が設定した質問に答える必要があります。
トラックバック
このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2005/12/10/172622/tb
Each FunctionDeclaration is processed during variable instantiation which happens when entering an execution context. The term "compile time" is misleading here.