IT戦記

ただただがむしゃらにソフト開発をしていたい

関数一発でプロトタイプチェーンに繋げて、オブジェクトをクローンする。

最終形がやたら複雑になっています

でも、実際はあそこまで複雑に書かなくても、できると思います ^^;

そう思う理由

  1. P 関数は object 関数の中でローカルのものである。コンストラクタとして使える必要はない。なので、 Atomic な場合でも P は function(){} でいい。
  2. method 関数では P.prototype のプロパティに関数が代入されているが、P はコンストラクタとして使われるのは一度だけなので、そこに追加する意味はない。つまり、関数の追加は できたオブジェクトに追加すればいい。ということは method 関数はいらない。

というわけで、僕も作ってみた。

toSource に対応してみた。あと、関数オブジェクトはクローンすると[[Call]]できなくなるので、同じ動作をする別のオブジェクトを返すようにした。undefined の場合と null の場合はそのまま返す。

// Executable
var clone = function(p) {
    p = (function(){return this}).apply(p); // objectify if atomic
    switch (typeof p) {
    case 'function':  return function() { return p.apply(this, arguments) };
    case 'undefined': return p;
    case 'object':
        if (p == null) return p;
        else {
            var f = function() {};
            f.prototype = p;
            var o = new f;
            switch (p.constructor) {
            case String: case Number: case Boolean:
                if (o.toSource) o.toSource = function() { return "clone(" + p.toSource.apply(p, arguments) + ")/*require clone function*/" };
                o.toString = function() { return p.toString.apply(p, arguments) };
                o.valueOf = function() { return p.valueOf.apply(p, arguments) };
            }
            return o;
        }
    }
};

動作確認

普通のオブジェクト
// Executable
var a = {};
var b = clone(a); // b のプロトタイプチェーンには a が継っている
a.hoge = 'fuga';
alert(b.hoge); // fuga と表示される
b.hoge = 'piyo';
alert(a.hoge); // fuga と表示される
数値をプロトタイプに繋げる。
// Executable
var a = 1;
var b = clone(a); // b のプロトタイプチェーンには a をオブジェクト化したものが継っている
b.addOne = function() { return this + 1 };
alert(b.addOne()); // 2
alert(a.addOne); // undefined
文字列をプロトタイプに繋げる
// Executable
var a = 'hello';
var b = clone(a); // b のプロトタイプチェーンには a をオブジェクト化したものが継っている
b.addAmachang = function() {return this + ' amachang'};
alert(b.addAmachang()); // 'hello amachang'
alert(a.addAmachang); // undefined
真理値をプロトタイプに繋げる
// Executable
var a = true;
var b = clone(a); // b のプロトタイプチェーンには a をオブジェクト化したものが継っている
b.toJapanese = function() {return this ? '本当' : 'うそ'};
alert(b.toJapanese()); // '本当'
alert(a.toJapanese); // undefined

実際にこの関数を使って new を使わないプロトタイプベースのオブジェクト指向を書いてみる。

もう、クラスベースみたいな new とかいらない!clone できるし!

// 動物のプロトタイプ
var animal = {
    breath: function() {
        alert('すーはー');
    }
};

// 鳥さんのプロトタイプ
var bird = clone(animal);
bird.fly = function() {
    alert('ばたばた');
};

// ペンギンさんのプロトタイプ
var penguin = clone(bird);
penguin.fly = function() {
    alert('飛べない');
};

// ドンペン君
var donpen = clone(penguin);
donpen.sing = function() {
    alert('どんどんどんどんきー♪ドンキーホーテー!');
};

donpen.breath(); // 動物だから呼吸できる
donpen.fly(); // ペンギンは飛べない
donpen.sing(); // ドンキホーテーの歌を歌う

// 実は鳥は歩ける
bird.walk = function() {
    alert('てくてく');
};

donpen.walk(); // 鳥なので「てくてく」歩ける

// でも、ペンギンは「てくてく」じゃなく「ぴょこぴょこ」だ
penguin.walk = function() {
    alert('ぴょこぴょこ');
};

donpen.walk(); // ぴょこぴょこ 

上のソースを実行

これぞ、プロトタイプベースオブジェクト指向

わーわー!まじで楽しい!