Ось моє рішення, яке засноване на стандартному прототип методи спадкування , описаної в Лоренца Полідорьте відповідь .
По-перше, я починаю з визначення цих допоміжних методів, які полегшують розуміння та читання пізніше:
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);
};
Тепер ви можете не тільки встановити суперклас класу за допомогою SubClass.setSuperclass(ParentClass)
, але і викликати перевизначені методи за допомогою 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())
Слід визнати, що навіть з допоміжними функціями синтаксис тут досить незручний. На щастя, в ECMAScript 6 було додано деякий синтаксичний цукор ( максимально мінімальні класи ), щоб зробити речі набагато гарнішими. Наприклад:
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())
Зверніть увагу, що ECMAScript 6 на даний момент все ще знаходиться на стадії проекту, і, наскільки мені відомо, він не реалізований в жодному великому веб-браузері. Однак, якщо ви хочете, ви можете використовувати щось на зразок компілятора Traceur для компіляції ECMAScript 6
до простого старого ECMAScript 5
JavaScript. Ви можете побачити наведений вище приклад, складений за допомогою Traceur тут .