1

Say i have the following object types :

var Positionable = function() { };
Positionable.prototype.x = 0;
Positionable.prototype.y = 0;

var Sizable = function() { };
Sizable.prototype.w = 0;
Sizable.prototype.h = 0;

var Fadable = function() { };
Fadable.prototype.a = 0;

I would like to have a function allowing me to create a sub type that would inherit from several other types. For example, i could create a type Rectangle that would inherit from both Positionnable and Sizable.

I could chain prototypes :

var Rectangle = function() { };
Sizable.prototype = new Positionable;
Rectangle.prototype = new Sizable;

But i don't like this method fo two reasons :

  • It causes Sizable to inherit from Positionable, which means that if i want another type to be Sizable without being Positionable, i can't do it;

  • It implies that a Sizable object is also Positionnable, which semantically isn't good since there is no reason for a Sizable object to be Positionnable and not the other way.

So i first thought about merging prototypes, assuming i have a function void merge( type dst, type src, bool overwrite ), in loop i would simply merge every base class prototype into the child type prototype. This would not create a chain (which is what i want as explained above)

But this causes another problem : since i'm not using new, the base constructors are not called. So i wonder, is there a way to also merge constructors ? For example, if i could access and reference the base constructors, i could store each of these base constructors in an array, and assign the child type constructor with a function calling each of these constructors consecutivly.

How could i do this with constructors ? Or maybe there is another way ?

Thanks for your help !

Virus721
  • 8,061
  • 12
  • 67
  • 123

2 Answers2

2

Since JavaScript doesn't support multiple inheritance you have to resolve to mixins. It's like you said, you have to copy the methods of one of the prototypes over to the child prototype.

The constructor functions should be called inside the child constructor function. Here is an example:

var Rectangle = function() {
    Sizeable.call(this);
    Positionnable.call(this);
};
// Rectangle inherits from Sizeable
Rectangle.prototype = Object.create(Sizeable.prototype);
// Mixin Positionnable
merge(Rectangle.prototype, Positionnable.prototype);

// alternatively you could mixin both of the prototypes

See also: Benefits of using `Object.create` for inheritance


function mixin(Target) {
    var args = arguments;

    // merge prototypes
    for(var i = 1; i < args.length; i++) {
        merge(Target.prototype, args[i].prototype);
    }

    // a new constructor function which calls all 
    // the passed constructor functions
    var F = function() {
        var instance = Target.apply(this, arguments);
        for (var i = 1; i < args.length; i++) {
            args[i].call(instance);
        }
        return instance;
    };
    F.prototype = Target.prototype;
    return F;
};

// usage

Rectangle = mixin(Rectangle, Sizeable, Positionnable);

This works under one assumption: The mixin constructors don't expect any arguments. If you have to pass arguments to them, you have to explicitly call them in the target/child constructor.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks for your help. I'll probably t do that if there is no other way, but the "problem" i see with this is that i have to do two things instead of one : 1) call th parent constructors in the child constructor, 2) merge prototypes. I would like to do all of this in a single function, but to be able to do that i would need to possibility to access and assign constructors. Is it possible ? Thanks. – Virus721 Aug 29 '13 at 05:07
  • @Virus721: You should always call the parent constructors inside the child constructor, since this is where all the instance specific initialization takes place. Of course if the parent constructors don't do anything (i.e. they don't contain any code), then you don't have to call them. You could automate the whole process if it's ok for you to redefine the `Rectangle` function. I will post an example. – Felix Kling Aug 29 '13 at 05:10
  • Dojo's mixin functionality came to mind reading this question, and handles constructor chains automatically. Depending on your needs and requirements I'd suggest looking into it. – Phix Aug 29 '13 at 05:20
  • Thanks for your answers. I'll have a look but i would like my library not to depend on anything. @Felix Kling I'll probably go that then. Besides it's more flexible as i can choose not to call a constructor with your method. – Virus721 Aug 29 '13 at 06:33
  • @Virus721: I'm glad I could help! If you need to support older browsers which don't support `Object.create`, include this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill. – Felix Kling Aug 29 '13 at 06:45
  • No it's a personnal project so i've no compatibility constraints. People have to update their browsers wor we'll still have IE7 in year 2050. – Virus721 Aug 29 '13 at 06:47
0
if you use Object.Create(),you have to think about browser compatibility. you can write
a function to resolve the issue.eg: 

     function multipleinheritPrototype() {
        if (arguments.length == 0) {
            return;
        }
        var args = arguments;
        var sub = args[0];
        var constructor = sub.prototype.constructor;
        if (!Object.create) {
            Object.prototype.create = function (obj) {

                function f() {
                }

                f.prototype = obj;
                return new f();
            };
        }
        var base;
        for (var i = 1; i < args.length; i++) {
            base = Object.create(args[i].prototype);
            for (var attr in base) {
                sub.prototype[attr] = base[attr];
            }
        }
        sub.prototype.constructor = constructor;

    }      
    you can such so use it.
    multipleinheritPrototype(Rectangle, Sizeable, Positionnable);
MingLi
  • 46
  • 2
  • `Object.create` is easily shimable if you only use it with the first argument: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill. Btw, you have to assign the function to `Object.create`, not `Object.prototype.create`. – Felix Kling Aug 29 '13 at 06:44
  • Thanks. Looks interesting too, i'll give it a try. – Virus721 Aug 29 '13 at 06:46