一から勉強させてください( ̄ω ̄;)

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです。

JavaScriptで関数に自分専用のプロパティを定義する方法

(2013/9/30追記)ここで書いた方法には「バグのあるコードや悪意のあるコードがカウンタを初期化したり、整数ではない値に設定したりすると整数値を返せなくなってしまう」という欠点もあるという記述が載っておりました。やはり、無難にクロージャを使って実装するのが一般的なようです。


この前、サイ本を読んでいて、「関数自身にプロパティを定義する方法」が載っていて、そういえばこんなことやったことないなーっと思ったので、今回はその事について書きたいと思います。

JavaScriptでは関数もオブジェクトなので、当然関数自身にプロパティを定義する事が可能です。

そこで関数にプロパティを定義する方法で、クロージャについてでやったような呼び出すたびに値が増えるような関数を実装してみたいと思います。


まず普通に関数スコープ内にローカル変数を格納しても以下のように失敗するので注意です。

var retInteger = function(){
    var i = 0;
    return i++;
}

retInteger(); //0
retInteger(); //0
retInteger(); //0


これはガベージコレクションによって使われなくなったオブジェクトが破棄されるからですね。

ローカル変数iは、retInteger関数呼び出しの終了とともに毎回破棄されているので、何度呼び出しても初期値0のままです。


ちなみに上記の問題は、

var i = 0;

var retInteger = function(){
    return i++;
}

retInteger(); //0
retInteger(); //1
retInteger(); //2


といったように、iをグローバル変数にする事で解決する事もできるのですが、関数内部でのみ使用する値をグローバルに定義するのはかっこ悪いし却下ですね。グローバル環境を気安く汚すやつは許しませんよ。


では関数にプロパティを作る方法で作り直してみます。こちら↓

var retInteger = function(){
    return retInteger.i++;
}

//ここで関数にプロパティiを定義
retInteger.i = 0;

retInteger(); //0
retInteger(); //1
retInteger(); //2

//グローバル変数iはできていない
window.i //undefined

こんな感じでグローバル変数を使わずに関数のプロパティに値を保持し続ける事ができました。


ちなみに

return this.i++;


のようにthisを使用する場合は

//間違い。thisがグローバル(strictモードだとundefined)になるので、iが未定義。undefinedに対して数値計算を要求したからNaNになる。
retInteger() //NaN 


//正しく動作する
retInteger.call(retInteger); //0
retInteger.call(retInteger); //1
retInteger.call(retInteger); //2


このようにthisの向き先が変わってしまうので注意が必要です。


いやーやはりサイ本は情報が盛りだくさんで素晴らしいですね。

量がハンパないので、暇なときにちょろっと読んでるだけですが、おもしろかった内容はこのようにどんどんアウトプットしていきたいと思いますのでよろしくお願いします。

JavaScript 第6版

JavaScript 第6版


小さな事からコツコツと。