クロージャとは、関数と変数環境をあわせたもので、Javaのようなメソッドとインスタンス変数を持ったクラスの動きをしてくれる。
つまり、変数の状態を記憶できるObjectのようなもので、また変数をprivateにできる。

クロージャは次の2点で実現できる。

  • 関数の中の関数を返す
  • 返した関数を変数に代入して、それを実行

例:

function doIfExistClass() {
    // privateなインスタンス変数
    var set = new Array();
    return function(val) {
        var pos = $.inArray(val, set);
        if (pos == -1) {
            set.push(val);
        } else {
            doSomething(val);
        }
    }
}

var doIfExist = doIfExistClass();
doIfExist(1);
doIfExist(2);
doIfExist(1); // doSomething(1)が実行される。

var doIfExist2 = doIfExistClass();
doIfExist2(1); // 別の環境が作られているため、doSomethingは実行されない。
doIfExist2(1); // doSomething(1)が実行される。

もしクロージャが一つだけあればよければ、doIfExistClassを即時関数(function(){...})()にして、関数定義と関数呼び出しをまとめて行い、いきなりdoIfExist変数に代入してもよい。
クラス名に相当する関数名が省けて少し便利。

var doIfExist = (function () {
    var set = new Array();
    return function(val) {
        var pos = $.inArray(val, set);
        if (pos == -1) {
            set.push(val);
        } else {
            doSomething(val);
        }
    }
})();

以上が最小構成のクロージャだが、メソッドが一つしかないため複数のメソッドを持つクラスを作れない。
これを解決するためには、クロージャが返す関数内関数の代わりに関数名がkeyで関数がvalueな連想配列で返してあげればよい。

function setClass() {
    var set = [];
    var getPosition = function(val) {
        return $.inArray(val, set);
    };

    return {
        // 追加
        add : function(val) {
            var notExists = !this.contains(val);
            if (notExists) {
                set.push(val);
            }
            return notExists;
        }
        // 削除
        , remove : function(val) {
            var pos = getPosition(val);
            if (pos > -1) {
                set.splice(pos, 1);
                return true;
            }
            return false;
        }
        // サイズ
        , size : function() {
            return set.length;
        }
        // すべての要素を削除
        , clear : function() {
            set = [];
        }
        // 指定された要素がセットに含まれている場合に true を返す。
        , contains : function(val) {
            return getPosition(val) > -1;
        }
    }
}

var set = setClass();
set.add(1);
set.add(2);
set.add(1);
set.size(); // 2
set.remove(2);
set.size(); // 1
set.contains(1); // true
set.contains(2); // false
set.clear();
set.size(); // 0