Save Expand icon

Ron Valstar
front-end developer

JavaScript inheritance and protected methods

I really like JavaScript, but it’s far from perfect. One of it’s imperfections is that you’ll have to resort to trickery to create private variables (iife). So what aboutinheritance and protected variables?

Lately I needed a base object to inherit multiple objects from. JavaScript does not really have classical inheritance so I always use prototypal inheritance (also, I don’t like this). But I might reconsider with es6 classes, even though it’s just sugarcoated prototypal.

Protect your privates

So… prototypal inheritance. We can fake private variables and methods through closures, but this will also prevent us from accessing them in child objects. So what we’re really looking for are protected variables and methods. There’s no such thing in JavaScript of course (no, not even in es6).
But we can fake that to.
The solution I came up with for my project is really an abstract base object. And it’s not really a base object but more a singleton factory. It does have a lot of bind, but it works. Enough talk, here’s some code:

/**
 * An example of protected methods and prototypal inheritance
 * @requires lodash
 * @param {string} name Call it something
 * @param {object} extend An object with functions to override
 * @returns {object}
 */
var baseFoo = (function(){
    var basePrototype = {
            protectedMethod: protectedMethod
            ,publicMethod: publicMethod
        }
        ,baseProperties = {}
    ;
    
    function protectedMethod(){
        return 'protectedMethod';
    }
    
    function publicMethod(){
        return 'publicMethod';
    }
    
    return function(name,extend){
        var inst = Object.create(basePrototype,baseProperties);
        _.extend(inst,{
            name: name||'noName'
            //
            ,expose: [] // The property the child object should return (could also be an object or a function).
            ,zuper: {} // Alas, super is reserved
        });
        //
        // create super
        for (var s in basePrototype) {
            if (basePrototype.hasOwnProperty(s)) {
                inst.zuper[s] = inst[s].bind(inst);
            }
        }
        Object.freeze(inst.zuper); // Because we can
        //
        // extend the instance
        if (extend) {
            for (var fncName in extend) {
                if (extend.hasOwnProperty(fncName)) {
                    inst[fncName] = extend[fncName].bind(inst);
                }
            }
        }
        //
        // extend expose property with public methods
        _.extend(inst.expose,{
            publicMethod: inst.publicMethod.bind(inst)
        });
        //
        // (do some more initialisation stuff like adding event listeners)
        //
        return inst;
    }
})();

/**
 * Here's a child object
 */
var childFoo = (function(){
    // call the factory method to get an instance
    var inst = baseFoo('childFoo',{
            protectedMethod: protectedMethod
        })
        // add other public methods to the expose property
        ,expose = _.extend(inst.expose,{
            otherPublicMethod: otherPublicMethod
        })
        ,zuper = inst.zuper
        // insert private variable here
    ;
    
    function protectedMethod(){
        return 'overriddenProtectedMethod'+zuper.protectedMethod();
    }
    
    function otherPublicMethod(){
        return 'otherPublicMethod'+protectedMethod();
    }
    
    // return inst.expose not the instance itself
    return expose;
})();

console.log(childFoo.publicMethod());
console.log(childFoo.otherPublicMethod());
console.log(childFoo.hasOwnProperty('publicMethod'));
console.log(childFoo.hasOwnProperty('protectedMethod'));

In the child object, instead of returning the instance, we return an instance property ‘expose’. This keeps the instance safely within the closure, effectively creating the illusion of protected variables and methods. There are limitations to this. The base object is abstract and the child objects are always final. Then again, you should never want to inherit too deep anyways.

And yes, it’s just a trick by convention, but it beats the hell out of prepending stuff with an underscore.

ps: here’s a fiddle