4

I am playing with classical inheritance in Javascript (in Chrome) following the example of Crockford. I like the parasitic interface but am looking for a bit cleaner way to encapsulate inheritance.

There are a couple additional requirements I am trying to meet:

  1. I'd like to be able to override methods of the parent class with methods in the child class that can call the parent method
  2. I don't want to have to re-declare properties of the parent class in the child class.

This is my parent class Parasite with an extend method that tries to encapsulate inheritance and take advantage of Object.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;
}

This is my child class, 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;
}

You can find my latest fiddle here (warning: infinitely recurses in Firefox, but not in Chrome):

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

The console output illustrates where the accessor issues are occurring.

klenwell
  • 6,978
  • 4
  • 45
  • 84
  • "*modify properties of the child class in the parent method*" does not sound like a good idea. What do you want that for? – Bergi Sep 17 '13 at 15:03
  • @Bergi I probably described this poorly. What I mean is I don't want to have to re-declare properties of the parent class in the child class. Question has been updated. – klenwell Sep 17 '13 at 16:39

1 Answers1

1

This isn't an answer to my question, per se, but here's a much simpler implementation that seems to meet my requirements. It follows the pattern presented in this 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 can be found here: http://jsfiddle.net/AvdK2/3/

Community
  • 1
  • 1
klenwell
  • 6,978
  • 4
  • 45
  • 84