読者です 読者をやめる 読者になる 読者になる

破棄されたブログ

このブログは破棄されました。

カリー化による関数の委譲とプライベート変数へのアクセス

複数のオブジェクトで同一の関数を継承ではなく委譲として再利用したい。 ただ、その関数がスコープチェインを通してプライベート変数へのアクセスをしていると関数をそのまま使いまわすことができない。

そこで、パラメータとしてアクセスしたいプライベート変数への参照を渡すようにすれば、プライベート変数アクセスが可能なクロージャができる。 ただし、ここでクロージャへ渡すのはプライベート変数への参照なので、プリミティブ値ではなくオブジェクトを渡さなければならない。

っていうのは当たり前のことなのに、なぜかカリー化したらよくねとか意味わからんこと思いついたんだけど、別にカリー化する必要なくね。 馬鹿なの死ぬの?

元のオブジェクト

fn 関数が返すオブジェクトは、set メソッドと get メソッドを持つ。
これらメソッドは、スコープチェインを通じてプライベート変数 container のプロパティ、container.value へのアクセスを行う。

var fn = function () {
    var o = {};
    // プライベート変数
    var container = {};

    // 使いまわしたい関数
    // プライベート変数へのアクセスを行う
    o.set = function (v) {
        var container.value = v;
    };

    o.get = function () {
        return container.value;
    };

    return o;
};

console.log(fn().set('hoge').get()); // "hoge"

この set メソッドを関数オブジェクトとして取り出して使いまわしたい。

名前空間オブジェクトの定義とカリー化関数の作成

名前空間オブジェクト Hateda のプロパティとして、カリー化関数を定義。
Object.prototype.curry とする方法もあるけど、組み込みオブジェクトを汚したくなかったので、こういう形で。

var Hateda = Hateda || {};
(function (h) {
    // カリー化関数
    // @param カリー化する
    // @param 可変長引数
    h.curry = function () {
        var slice = Array.prototype.slice;
        var args  = slice.apply(arguments, [1]);
        var fn    = arguments[0];

        return function () {
            return fn.apply(null, args.concat(slice.apply(arguments)));
        };
    };

    // 第一引数を 'hoge' で固定してカリー化
    var curried = h.curry(function () {
        console.log(arguments);
    }, 'hoge');

    // カリー化を確かめる
    curried('fuga'); // ["hoge", "fuga"]
})(Hateda);

共有する関数

先ほどの set メソッドを再利用可能なクロージャとして取り出す。
set メソッドはプライベート変数への参照をスコープチェインで持っていたけど、
この shareFn 関数は、第一引数として参照を保持する。

(function (h) {
    // 共有する関数
    h.shareFn = function (to, from) {
        to.value = from;
        return to;
    };
})(Hateda);

基底オブジェクトの定義

get を派生オブジェクトで書くのがダルいから、基底オブジェクト h.obj を定義した。
継承パターンには JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス の関数型を使う。

(function (h) {
    // 基底オブジェクト生成関数
    h.obj = function (o) {
        // 基底オブジェクト
        var o = o || {};

        o.get = function () {
            return o.value;
        };

        return o;
    };
})(Hateda);

カリー化を使った関数の委譲

カリー化関数がクロージャを返すから、オブジェクト内での定義に必要なコード量は減る。

(function (h) {

    var ex = function () {
        // h.obj を継承するオブジェクトを生成
        var o = h.obj({});

        // 共有する関数でプライベート変数を操作させる関数
        o.set = h.curry(h.shareFn, o);

        return o;
    };

    console.log(ex().set('hoge').get()); // "hoge"

})(Hateda);

関数を呼び出す関数を普通に定義

(function (h) {

    var objs = [];

    for (var i = 0; i < 2; i++){
    var ex = function () {
        // h.obj を継承するオブジェクトを生成
        var o = h.obj({});

        // 共有する関数でプライベート変数を操作させる関数
        o.set = function (value) {
            return h.shareFn(o, value);
        };

        return o;
    };

    console.log(ex().set('hoge').get()); // "hoge"

})(Hateda);

カリー化を使ってたのってやってることはこれと同じだし、中途半端にカリー化するよりはオブジェクト化して切り分けちゃったほうがわかりやすいよねえ。
そうすると普通に Strategy パターンになるのかな? 何考えてたんだろ…


まるまる一ヶ月以上更新サボってた…

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

広告を非表示にする