JavaScript Diary

関数の宣言方式で何が違うのか [ 2001/06/03 ]

関数の宣言の方法には、一般的な functionキーワードを先頭に記述する function文、Functionコンストラクタ関数を使用したもの、そして関数リテラルを使用したものがあります。

function f(x){ return x*x; }
var f = new Function("x", "return x*x;");
var f = function(x){ return x*x; };

この3式は同じことをしているのですが、実際は微妙に違います。

それは function文とリテラルが静的なものであるのに対して、コンストラクタを使ったものは静的でないということです。
コンストラクタ関数の場合、JavaScript コードを動的に生成することが可能なため
コンパイルされるのも実行される直前です。
また一種のクラスであると考えると new するたびに新たな関数オブジェクトを生成するので
メモリもその都度消費します。
このため for 文などのループ内でこの記述をすることは実行スピードも含め、あまり良くないということになります。
それに比べ関数リテラルの場合はプログラムの静的な構造の一部となるため、
再コンパイルされることもなく、関数オブジェクトが新たに生成されることもありません。
Function コンストラクタの利点は一行でまとめることが可能なため、場合によってはすっきりしたコードになることがあります。
また、動的なコード生成が可能であることを利用すると
他言語では難しいとされる特殊なプログラムを簡単に組むことが出来るようになります。

もう一つ重要なことは Functionコンストラクタは動的なコード生成が可能という性質上、
実行コンテキストは常にグローバル実行コンテキストになるということです。
つまり、Functionコンストラクタで指定した引数の使用している識別子等は全てグローバルな値を参照します(thisはwindowオブジェクトを参照します)。これはeval関数, setTimeout関数等と同じ性質を持つということです。
 これに対し functionリテラルは静的な構造の一部という言葉から分かるように
コンテキストは一意に現在実行されているコンテキストになります。
さて、僕自身この段落で何が言いたいのかよく分かりません(滅)以下に具体的な例を示します。

var x = 1;

function f(){
    var x = 0;
    
    var f0 = new Function("return x;"); // このxはグローバル変数のxを参照
    var f1 = function(){ return x; };   // このxはローカル変数のxを参照
    
    alert(f0());
    alert(f1());
}

f();

これを実行すると最初に1が表示され次に0が表示されます。
この例から分かるように Functionコンストラクタ と functionリテラルを同等のものとして扱うことは非常に危険です。
殆どの場合はfunctionリテラルでこと足りるでしょう。

ちなみに function 文は JavaScript1.0、コンストラクタ関数は JavaScript1.1、関数リテラルは JavaScript1.2 から対応しています。