バカしか落ちない落とし穴

昨日の日記には落とし穴があった.

// 配列を任意の値で初期化するメソッド
Array.prototype.init = function (value) {
    for (var i = 0; i < this.length; i++) {
    	this[i] = value;
    }
};

はちゃんと動かない

var array = (new Array (3)).init ("init");
for (var a in array) { document.writeln (array[a]); }
// 結果 : undefined undefined undefined

で,問題を簡単にしてみるため,以下の例を考える.

Hoge型のオブジェクトをつくるコンストラクタ,Hoge関数を定義する.

Hoge型はfugaメソッドを持ち,fugaメソッドはオブジェクトに対してfoobarフィールドを生成する.

var Hoge = function () {
    this.fuga = function () {
        this.foobar = 3;
    };
};

var hoge = (new Hoge ()).fuga ();

と書くとする.

これで,fugaの実行されたHogeのオブジェクト,つまりhogeメソッドとfoobarフィールドを持つHogeオブジェクトができる.わけない.

なぜかというと,それはHoge関数に問題があるからである.

この"var hoge = (new Hoge()).fuga ();"の評価がどう行われたかを検証するとすぐわかる.

評価モデルで考える.まず,式は3つのブロックに分けられる.

"var hoge", "=" "(new Hoge ()).fuga ();"のブロックにわけられる.

最初の"var hoge"ブロックから評価すると,この式はhogeという変数を作成する.

この時点で変数は値を持たない.

次に,"="演算子(以下イコール演算子)のブロック.

イコール演算子は自身の右辺の結果,つまり右辺を評価して最後に戻ってくるものを左辺の変数やフィールドに束縛(むすびつける)する.

最後に"(new Hoge ()).fuga ();"のブロック.

この式の意味を言語化すると,

"(new Hoge ())"を評価し,戻ってきたHogeオブジェクト(もちろん,まだ変数名は無い)ののfugaメソッドを実行し,fugaメソッドの戻り値を返す.

そう,この式の評価結果はfugaメソッドなのである.

ということはイコール演算子の右辺に渡される値はfugaの実行結果である.

fugaは返り値を持たないメソッドである.

これにより,hogeは"undefined"が束縛される.というか,そのままか.

よって,うまく動かない.

で,どうしたらいいか.

もちろん,こうする

var Hoge = function () {
    this.fuga = function () {
        this.foobar = 3;
	return this;
    };
};

こうすうると,

var hoge = (new Hoge ()).fuga ();

のようなオブジェクトを生成しながらメソッドを実行する場合でも問題ない.

また,

var hoge = new Hoge ();
hoge.fuga ();

のような手続きにおいても同様の結果が得られる.

まとめとして,最初に示した式は

// 配列を任意の値で初期化するメソッドの修正版
Array.prototype.init = function (value) {
    for (var i = 0; i < this.length; i++) {
    	this[i] = value;
    }
    return this;
};

のようにすれば,かなり問題ナッシングになる.



また一つ,バカから前進しました.

めでたしめでたし.