4

Crockfordの例に従って、Javascript (Chrome) で古典的な継承を使用しています。私は寄生インターフェースが好きですが、継承をカプセル化するためのもう少しクリーンな方法を探しています。

私が満たそうとしている追加の要件がいくつかあります。

  1. 親クラスのメソッドを、親メソッドを呼び出すことができる子クラスのメソッドでオーバーライドできるようにしたい
  2. 子クラスで親クラスのプロパティを再宣言する必要はありません。

これは、継承をカプセル化して利用しようとParasiteするメソッドを持つ私の親クラスです。extendObject.defineProperty

function Parasite(host) {
    var self = {};
    self.host = host;
    self.swollen = false;

    self.init = function() {
        console.debug('Parasite.init');
        return self;
    };

    self.suck = function() {
        console.log("I'm a parasite who sucks on " + self.host);
        self.swollen = true;
        return self;
    };

    self.extend = function(child) {        
        for(var prop in self) {
            if (prop == 'extend') {     // skip extend
                console.debug('skip extend');
                continue;
            }

            var is_extended = child.hasOwnProperty(prop);
            var is_accessor = typeof self[prop] != "function";

            // inherit prop
            if (! is_extended) {
                child[prop] = self[prop];
                console.debug('prop', prop, 'inherited by child');
            }
            // default: override
            else {
                console.debug('prop', prop, 'overridden by child');
            }

            // For accessors, parent should reference child. This tries to
            // synchronize them on the child's accesor.
            if (is_accessor) {
                var accessor = prop.toString();
                console.warn('define accessor for', accessor, ':', child[accessor]);
                Object.defineProperty(self, accessor, {                      
                    get: function() {
                        var val = child[accessor];
                        console.debug('getting', accessor, val, 'from', child, 'for', self);
                        return val;
                    },
                    set: function(val) { 
                        console.debug('setting', accessor, val, 'from', child, 'for', self);
                        child[accessor] = val;
                    },
                    enumerable: true,
                    configurable: true
                });
            };
        }

        child.parent = self;
        return child;
    };

    self = self.init();
    return self;
}

これは私の子クラスですTick:

function Tick(host) {
    var self = {};

    self.suck = function() {
        self.parent.suck.call();
        self.engorge();
    };

    self.engorge = function() {
        console.log("And now I'm engorged with blood.");
    };

    self.init = function() {
        var parent = new Parasite(host);
        self = parent.extend(self);
        return self;
    };

    self = self.init();
    return self;
}

ここで私の最新のフィドルを見つけることができます (警告: Firefox では無限に再帰しますが、Chrome ではそうではありません):

http://jsfiddle.net/SSDgv/23/

コンソール出力は、アクセサーの問題が発生している場所を示しています。

4

1 に答える 1

1

これは、それ自体が私の質問に対する答えではありませんが、私の要件を満たすと思われるはるかに単純な実装を次に示します。この Stack Overflow answerに示されているパターンに従います。

function Parasite(host) {
    var self = {};
    self.host = host;
    self.hungry = true;

    self.init = function() {
        console.debug('Parasite.init');
        return self;
    };

    self.suck = function() {
        console.log("I'm a parasite who sucks on " + self.host);        
        self.hungry = false;
        return self;
    };

    self.init();
    return self;
}

function Tick(host) {
    var self = new Parasite(host);
    self.engorged = false;

    var base_suck = self.suck;
    self.suck = function() {
        base_suck();
        self.engorge();
    };

    self.engorge = function() {
        console.log("And now I'm engorged with blood.");
        self.engorged = true;
    };

    self.init = function() {};

    self.init();
    return self;
}

Fiddle with tests はここにあります: http://jsfiddle.net/AvdK2/3/

于 2013-09-16T19:08:32.733 に答える