13

How can I go about making a child class override a privileged method of a base class?

If its not possible, is there another way to achieve what I am trying to accomplish in the simple code example below?

I cannot convert the baseclass function parseXML() to public because it requires access to private variables

    function BaseClass()
    {
        var map = {};

        // I cannot make this function public BECAUSE it accesses & changes private variables
        this.parseXML = function( key, value )
        {
            alert("BaseClass::parseXML()");
            map[key] = value;
        }
    }

    function ChildClass()
    {
        BaseClass.call(this);
        this.parseXML = function( key, value, otherData )
        {
            alert("ChildClass()::parseXML()");

            // How can I call the base class function parseXML()?
            //this.parseXML();  // calls this function not the parent function
            //MyClass.prototype.doStuff.call
            BaseClass.prototype.parseXML.call(this, key, value);  // fails
            //BaseClass.prototype.parseXML(); // fails

            // perform specialised actions here with otherData
        }
    }

    ChildClass.prototype = new BaseClass;

    var a = new ChildClass();
    a.parseXML();
sazr
  • 24,984
  • 66
  • 194
  • 362
  • possible duplicate of [Possible to override a base classes privileged function?](http://stackoverflow.com/questions/8030884/possible-to-override-a-base-classes-privileged-function) ... please edit your original question instead. – Felix Kling Nov 07 '11 at 00:23
  • 2
    Suggestion: Don't try to force method visibility into JavaScript. It is not designed to have private and protected methods. Document your code properly. Mark this method as private in the comment. If someone uses the method anyway and therefore breaks his code when he uses your library -> his problem. – Felix Kling Nov 07 '11 at 00:43
  • possible duplicate of [Calling base method using javascript prototype](http://stackoverflow.com/questions/560829/calling-base-method-using-javascript-prototype) – outis Nov 07 '11 at 06:24

2 Answers2

16
function BaseClass() {
    var map = {};
    this.parseXML = function(key, value) {
        alert("BaseClass::parseXML()");
        map[key] = value;
    }
}

function ChildClass() {
    BaseClass.call(this);
    var parseXML = this.parseXML;
    this.parseXML = function(key, value, otherData) {
        alert("ChildClass()::parseXML()");
        parseXML.call(this, key, value);
    }
}

ChildClass.prototype = new BaseClass;

var a = new ChildClass();
a.parseXML();

Live Example

Basically you cache the privileged method (which is only defined on the object) and then call it inside the new function you assign to the privileged method name.

However a more elegant solution would be:

function BaseClass() {
    this._map = {};
};

BaseClass.prototype.parseXML = function(key, value) {
    alert("BaseClass::parseXML()");
    this._map[key] = value;
}

function ChildClass() {
    BaseClass.call(this);
}

ChildClass.prototype = Object.create(BaseClass.prototype);
ChildClass.prototype.parseXML = function(key, value, otherData) {
    alert("ChildClass()::parseXML()");
    BaseClass.prototype.parseXML.call(this, key, value);
}

var a = new ChildClass();
a.parseXML();

Live Example

Also bonus implementation using pd

Raynos
  • 166,823
  • 56
  • 351
  • 396
-4

IMO, you need to use a Javascript library like Ext Js to simplify this task. Anyway, the following example illustrates how you can write some helper methods. It's a part of an unreleased open source project that I'm working on.

var JWObject = (function () {

    var jwobj = function (){};

    jwobj.prototype = { };

    return jwobj;

})();

var Prototype = (function () {

    var scopeQueue = [ window ];

    return {

        beginScope: function (namespace) {
            var parts = namespace.split('.');
            for (var i = 0; i < parts.length; i++) {
                var name = parts[i],
                    parent = this.getScope(),
                    part = parent[name];

                if (part && !part.__namespace) {
                    throw Error('/* ERROR MESSAGE */');
                }

                scopeQueue.push(parent[name] = (part || { __namespace: true }));
            }
        },

        endScope: function () {
            if (scopeQueue.length > 1) {
                scopeQueue.pop();
            }
        },

        getScope: function () {
            return scopeQueue.pick();
        },

        define: function (name, members) {

            var scope = this.getScope();

            if (scope[name]) {
                throw Error('The prototype already exist.');
            }

            this.extend(members, {
                scope: scope,
                extend: JWObject,
                statics: {}
            });

            // Getting constructor
            var ctor = (members.constructor === Object) ? function() { } : members.constructor;
            delete members.constructor;

            if (typeof members.extend === 'string') {
                members.extend = scope[members.extend];
            }

            if (!members.extend) {
                throw Error('The base class is not specified.');
            }

            // Deriving from parent type
            ctor.prototype = new members.extend();
            members.super = members.extend.prototype;
            delete members.extend;

            members.statics.__class = true;
            this.extend(ctor, members.statics, true);
            delete members.statics;

            // Adding new members
            this.extend(ctor.prototype, members, true);

            // Adding and returning the created prototype
            return scope[name] = ctor;

        },

        extend: function (expando, members, override) {
            for (var m in members) {
                if (override || !expando[m]) {
                    expando[m] = members[m];
                }
            }
        }

    };

})();

Prototype.extend(Array.prototype, {

    pick: function() {
        return this[this.length - 1];
    }

});

Here is the result:

Prototype.beginScope('Sample');

/**
 * Prototype: Sample.Plugin
 */
Prototype.define('Plugin', {

    init: function() {
        alert('init!');
    }

});

Prototype.beginScope('Extension');

/**
 * Prototype: Sample.Extensions.Plugin
 * Extend   : Sample.Plugin
 */
Prototype.define('Foo', {
    extend: Sample.Plugin,

    init: function() {
        this.super.init.call(this);
        alert('child: init!');
    },

    fun: function() {
        this.init();
    },

    statics: {

        create: function() {
            return new Sample.Extension.Foo();
        }

    }

});

Prototype.endScope();
Prototype.endScope();

As you can see in the preceding code, the Prototype object provides some functionality to defining a namespace (Prototype.beginScope, Prototype.endScope and Prototype.getScope) or defining a prototype (Prototype.define).

You can inherit a prototype from another using extend like java.

Prototype.define('Foo', {
    extend: Sample.Plugin,

Or call the base class method as follows:

    init: function() {
        this.super.init.call(this);

Also, every prototype you define with above code will be derived from JWObject by default.

m3kh
  • 7,881
  • 2
  • 31
  • 37
  • @Raynos - +1 for your -1. But you missed "muddled" from the list of adjectives. – RobG Nov 07 '11 at 02:29
  • @Raynos and other downvoters Unfortunately, I can't downvote my shamelessly recommendation. But regardless of fun, I see this system from my point of view as a RIA developer. Anyway I'd love to know a good JavaScript library circumstances from yours point of view. :) THANKS – m3kh Nov 07 '11 at 03:38
  • @MehdiGolchin [pd](https://github.com/Raynos/pd) would be a solid example of light weight OO sugar for javascript. The only thing missing is `super` and there is no good mechanism for super. – Raynos Nov 07 '11 at 03:43