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 6
się do zwykłego, starego ECMAScript 5
JavaScript. Możesz zobaczyć powyższy przykład skompilowany przy użyciu Traceur tutaj .