Traditionally, in JavaScript, it is better to avoid multiple inheritance and use mixins when necessary to share functions. Multiple inheritance leads to all kinds of problems, including the notorious problem, the terrible diamond threat (the problem you are facing).
Different languages ββuse different methods to solve the diamond problem. For example, Java uses interfaces instead of multiple inheritance, C ++ uses virtual inheritance to eliminate ambiguities, and languages ββsuch as Python and Dylan use the method resolution order to linearize heterarchy .
However, all of the above methods are not effective enough to solve the diamond constructor problem that you are facing. Interfaces are useless in dynamic languages ββsuch as JavaScript: duck printing plays a more significant role. Virtual inheritance unnecessarily complicates the situation. Linearization resolves the order of inherited methods, but does not solve your problem.
You can use two methods in JavaScript:
- Abandon multiple inheritance in general and use mixins to share functions, as most JavaScript programmers do.
- Conditionally call the constructor of the base class from the constructor of the derived class, depending on whether
this
instance of the constructor of the derived class.
I would recommend using mixins. However, the final decision is yours. On my behalf, Iβll just talk about the advantages and disadvantages of each technique.
Impurities
Mixins is a traditional way to implement multiple inheritance in JavaScript, and in most cases, traditional methods have proven to be the best. Smeshin is similar to Java s interface implementation. You could (and should, in my humble opinion) reorganize your code as follows:
function Element() { // constructor logic } function positionable(that, x, y) { that.x = x; that.y = y; } function sizable(that, w, h) { that.w = w; that.h = h; } function Rectangle(x, y, w, h) { Element.call(this); positionable(this, x, y); sizable(this, w, h); } Rectangle.prototype = Object.create(Element.prototype);
As you can see in the above code, positionable
and sizable
are not constructors. These are mixins - you should not use them to create instances. You use them to expand instances.
In JavaScript, it is best to create classes that inherit from the same base class, and use mixins to share additional features as needed.
Bonus: Programming is very similar to English. Nouns are like classes, verbs are like methods, and adjectives are like mixins. Words ending in "capable" are usually adjectives. Therefore, positionable
and sizable
must be implemented as mixins.
Classes
If you still intend to implement positionable
and sizable
classes as classes, I am afraid that I cannot do this to change my mind. Therefore, I will show you how to solve your problem with classes and demonstrate why this method is inferior to mixins use methods:
function Element() { this.traits = []; } function Position() { if (this instanceof Position) Element.call(this); this.traits.position = { x: 0, y: 0 }; } Position.prototype = Object.create(Element.prototype); function Sizable() { if (this instanceof Sizable) Element.call(this); this.traits.size = { w: 0, h: 0 }; } Sizable.prototype = Object.create(Element.prototype); function Rectangle() { Element.call(this); Positionable.call(this); Sizable.call(this); } Rectangle.prototype = Object.create(Element.prototype); _.extend(Rectangle.prototype, Positionable.prototype, Sizable.prototype);
As you can see, this code is definitely uglier than code written using mixins. Inside the positionable
and sizable
we check if this
instance of the corresponding constructors before calling the Element
constructor, which solves the problem of the diamond constructor.
We use the Underscore.js extend
function to copy the Positionable.prototype
and Sizable.prototype
to Rectangle.prototype
. I will leave this to you to understand why this is not the recommended method for implementing multiple inheritance.