nitesh sharma
nitesh sharma

Reputation: 601

javascript inheritance pattern confusion

I find this is most recommended way to do inheritance in javascript.

function extend(Child, Parent) {
 var F = function(){};
 F.prototype = Parent.prototype;
 Child.prototype = new F();
}

what if I already have methods in child's prototype, aren't they will overwrite, shouldn't we preserve them.

function extend(Child, Parent) {
 var c = child.prototype;
 var oldProto = new C();  
 var F = function(){};
 F.prototype = Parent.prototype;
 Child.prototype = new F();
 for(var i in oldProto ){
   Child.prototype[i] = oldProto[i]  
 }  
}

Upvotes: 1

Views: 269

Answers (3)

Xitalogy
Xitalogy

Reputation: 1612

The code below is the one of the best I have seen for doing inheritance in JavaScript.

Object.create(proto [, propertiesObject ]) is discussed on MDN here.

Below, Jon defines a base empty object called ExtendBase then adds a function property called extend which is not enumerable which takes as its argument a single new object.

That object should contain enumerable properties such as methods and data that will be added to the base object.

He gets all the enumerable properties from the passed object, then creates an array of the necessary descriptors to pass into Object.create using those properties' names. He then uses the parent object as the prototype and resultant descriptors as new properties to be added to the child object directly in the Object.create() call.

As you can see, you can use an object argument with properties, including methods, to extend a parent without losing that passed object's properties with the result being a child object with the parent as the prototype and the enumerable objects of the passed object added directly to the child.

However, this maintains a clean prototype chain while intending to extend parent objects using other objects which are created sanely to extend the parent into a new child in a way that makes sense:

Live sample here (Press F12 in Chrome for console output, or use FireBug in FireFox, etc.)

JavaScript:

// Original Author:  FireFly - Jonas Höglund - ##javascript channel
// on irc.freenode.net - see THANKS File.  Updated to private data
// members and passable initial parameters by Scott Sanbar

///////////////
// Library code
///////////////

var ExtendBase = {};

Object.defineProperty(ExtendBase, 'extend', {
    enumerable:false, value:function (obj) {
        'use strict';

        var descs = {};

        Object.getOwnPropertyNames(obj).forEach(function (key) {
            descs[key] = Object.getOwnPropertyDescriptor(obj, key)
        });

        return Object.create(this, descs);
    }
});

///////////////
// Sample Usage
///////////////

function PersonObj(nam) {

    return {
        name:new function () {

            var name = nam;

            this.set = function (value) {
                name = value;
            };

            this.get = function () {
                return name;
            }
        },

        // A person can tell you its name.
        talk:function () {
            return "Hello, I'm " + this.name.get();
        }
    }
}
;

function WorkingPersonObj(occ) {

    return {

        occupation:new function () {

            var occupation = occ;

            this.set = function (value) {
                occupation = value;
            };

            this.get = function () {
                return occupation;
            }
        },

        // A working person also tells you their occupation when they talk.
        talk:function () {
            return Person.talk.call(this) + " and I am a " + this.occupation.get();
        }
    }
}
;

var hush = {

    hush:function () {
        return "I am supposed to be quiet";
    }
};

var Person = ExtendBase.extend(new PersonObj('Harry'));
var WorkingPerson = Person.extend(new WorkingPersonObj('wizard'));

var wp1 = WorkingPerson.extend(hush);

console.log(wp1.talk()); // "Hello, I'm Harry and I am a wizard"
console.log(wp1.hush()); // "I am supposed to be quiet"
wp1.name.set("Elijah");
wp1.occupation.set("prophet");
console.log(wp1.talk()); // "Hello, I'm Elijah and I am a prophet"
console.log(wp1.name.get());
console.log(wp1.occupation.get());

Upvotes: 0

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76413

I'm not sure if this is any good to you, but it's well important to remember: prototypes are not the same things as classes. What you're doing is trying to make JS behave like a traditional OO language, which is trying to teach a dolphin to dance ballet, or forcing a tiger to become vegan: Admirable, but destined to end in tears.

I can't really see why you'd want to use the extend function to do whatever it is you're trying to do. Why not simply use this:

function Parent()
{};
function Child()
{};
//augment parent proto
Parent.prototype.parentMethod1 = function()
{};
//set Child's proto to Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
//Then augment the Child's prototype
Child.prototype.childMethod1 = function()
{};
var foo = new Child();
foo.parentMethod1();//works
foo.childMethod1();//works, too

IMO, this solves the problem entirely. Sure, it's a tad more verbose, but OOP always is.

Upvotes: 2

bjornd
bjornd

Reputation: 22941

The pattern you're trying to achieve is called multiple inheritance. And it's highly not recommended for the use because of the issue you're experiencing, called diamond problem. Just use mixin pattern instead.

Upvotes: 0

Related Questions