JavaScript doesn't natively support multiple inheritance, but we can use a design pattern called a mixin.
A mixin is a design pattern where you iterate over multiple objects properties to combine them into a single new object.
function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}
And now you have multiple inheritance.
This method allows us to inherit from(extend) as many other object's as we want.
function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}
function Taco(){
}
Taco.prototype.eat = function(){ /*do stuff*/ };
function Beans(){
}
Beans.prototype.cook = function(){ /*do stuff*/ };
function Guacomole(){
}
Guacomole.prototype.spread = function(){ /*do stuff*/ };
extend(Taco.prototype, Beans);
console.log(Taco);
By using the extend function to implement the mixin pattern we created references to the super prototype methods.
Also notice that we don't inherit a constructor with the mixin
Since we're adding methods to the objects existing prototype, without Object.create() to assign a new prototype object, the constructor remains intact.
var canFly = function() {
this.price = 9.99;
this.takeOff() = function() {
//fly baby
};
return this;
};
var isSubmersible = function() {
this.oxygenTanks = 4;
this.dive = function() {
//go deep
};
return this;
}
var Car = function(opts) {
this.wheels = 4;
if(opts.fly){
canFly.call(Car.prototype);
}
if(opts.swim){
isSubmersible.call(Car.prototype);
}
};
var flyingSubmaringCar = new Car({ fly: true, swim: true});
"A mixin should be a process not an object"
Although this achieves multiple inheritance best practice says:
"Something that adds behavior"
If any object implements an interface it must provide certain behavior and properties
If you wanna be in the band, you gotta wear the makeup bro...
Easiest Way To Remember Interfaces
class Car {
private _basePrice: number;
engine: IEngine;
constructor(options: IAutoOptions) {
this.basePrice = options.basePrice;
this.engine = options.engine;
}
drive() : number {
console.log('driving');
}
get price(): number {
return this._basePrice;
}
set price(value: number) {
this._basePrice = value;
}
}
interface IEngine {
start() : void;
stop() : void;
}
interface IAutoOptions {
engine: IEngine;
basePrice: number;
}
interface ICanFly {
altitude: number;
fly() : void;
}
class FylyingEngine implements IEngine {
start() : void { }
stop() : void { }
}
class FlyingCar extends Car implements ICanFly {
constructor({basePrice: number, engine: FlyingEngine}){
super(basePrice, engine);
this.altitude = 0;
};
fly() : void {
this.engine.start();
console.log('flying');
}
}
Remember an interface is "just a contract".
By abiding by the contract your object will implement all the requirements of the contract