Oto moje rozwiązanie, które opiera się na standardowym prototypowego dziedziczenia metody opisanej w Lorenzo Polidori za odpowiedź .
Najpierw zacznę od zdefiniowania tych pomocniczych metod, które później ułatwiają zrozumienie i czytelność:
Function.prototype.setSuperclass = function(target) {
this._superclass = target;
this.prototype = Object.create(this._superclass.prototype);
this.prototype.constructor = this;
};
Function.prototype.getSuperclass = function(target) {
return this._superclass;
};
Function.prototype.callSuper = function(target, methodName, args) {
if (arguments.length < 3) {
return this.callSuperConstructor(arguments[0], arguments[1]);
}
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
var method = superclass.prototype[methodName];
if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");
return method.apply(target, args);
};
Function.prototype.callSuperConstructor = function(target, args) {
if (args === undefined || args === null) args = [];
var superclass = this.getSuperclass();
if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");
return superclass.apply(target, args);
};
Teraz nie tylko możesz ustawić nadklasę klasy za pomocą SubClass.setSuperclass(ParentClass), ale możesz także wywołać metody nadpisane za pomocą SubClass.callSuper(this, 'functionName', [argument1, argument2...]):
function Transform() {
this.type = "2d";
}
Transform.prototype.toString = function() {
return "Transform";
}
function Translation(x, y) {
Translation.callSuper(this, arguments);
this.x = x;
this.y = y;
}
Translation.setSuperclass(Transform);
Translation.prototype.toString = function() {
return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
function Rotation(angle) {
Rotation.callSuper(this, arguments);
this.angle = angle;
}
Rotation.setSuperclass(Transform);
Rotation.prototype.toString = function() {
return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}
translation = new Translation(10, 15);
console.log(translation instanceof Transform);
console.log(translation instanceof Translation);
console.log(translation instanceof Rotation);
console.log(translation.toString())
Trzeba przyznać, że nawet w przypadku funkcji pomocniczych składnia jest tutaj dość niezręczna. Na szczęście w ECMAScript 6 dodano trochę cukru syntaktycznego ( maksymalnie minimalne klasy ), aby uczynić rzeczy znacznie ładniejszymi. Na przykład:
class Transform {
constructor() {
this.type = "2d";
}
toString() {
return "Transform";
}
}
class Translation extends Transform {
constructor(x, y) {
super();
this.x = x;
this.y = y;
}
toString() {
return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
}
}
class Rotation extends Transform {
constructor(angle) {
super(...arguments);
this.angle = angle;
}
toString() {
return super(...arguments) + this.type + " Rotation " + this.angle;
}
}
translation = new Translation(10, 15);
console.log(translation instanceof Transform);
console.log(translation instanceof Translation);
console.log(translation instanceof Rotation);
console.log(translation.toString())
Zauważ, że ECMAScript 6 jest nadal w fazie roboczej i o ile wiem, nie jest zaimplementowany w żadnej większej przeglądarce internetowej. Jeśli jednak chcesz, możesz użyć czegoś takiego jak kompilator Traceur, aby skompilować ECMAScript 6się do zwykłego, starego ECMAScript 5JavaScript. Możesz zobaczyć powyższy przykład skompilowany przy użyciu Traceur tutaj .