Hallo Christian
Ich hätte es ähnlich umgesetzt. Mir fiele auch keine andere Methode ein, das zu bauen, deshalb war ich ein wenig erstaunt ob Orloks Empfehlung ;-)
Äh, warum genau? – Ich meine, dir ist schon klar, dass die gezeigte Variante nicht das gewünschte Ergebnis bringen wird, wenn es sich bei der Basisklasse um den Konstruktor Array
handelt, oder?
Nehmen wir mal dieses simple Beispiel, das valides ECMAScript 6 ist:
class CustomArray extends Array {
method ( ) {
// do something
}
}
const instance = new CustomArray;
console.log(Array.isArray(instance)); // true
In TypeScript notiert und nach ECMAScript 5 übersetzt ergibt das:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) {
if (b.hasOwnProperty(p)) {
d[p] = b[p];
}
}
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var CustomArray = (function (_super) {
__extends(CustomArray, _super);
function CustomArray() {
_super.apply(this, arguments);
}
CustomArray.prototype.method = function () {
// do something
};
return CustomArray;
}(Array));
var instance = new CustomArray;
console.log(Array.isArray(instance)); // false
Man beachte jeweils die letzte Zeile. ;-)
Wenn wir jetzt den ganzen unwichtigen Kram weglassen und uns auf das Wesentliche konzentrieren, wie ich das in meiner ersten Antwort bereits versucht habe, dann bleiben diese Zeilen:
function CustomArray() {
_super.apply(this, arguments);
}
Hier wird also der Konstruktor Array
im Kontext des Objektes aufgerufen, das durch den Aufruf des Konstruktors CustomArray
automatisch erzeugt wurde. Da es dem Konstruktor Array
aber herzlich egal ist, in welchem Kontext er aufgerufen wird, wird hier im Ergebnis einfach nur ein gewöhnliches Array erzeugt, das mangels Referenz von der Garbage Collection auch gleich wieder einkassiert wird. Zurückgegeben wird von CustomArray
schließlich ein planes Objekt und kein Array.
Wie dedlfix sagte, handelt es sich bei den in ECMAScript 6 eingeführten Klassen im Großen und Ganzen bloß um syntaktischen Zucker, aber eben nicht nur.
class CustomArray extends Array {
constructor ( ) {
super(...arguments);
console.log(Array.isArray(this));
}
}
const instance = new CustomArray; // true
Durch den expliziten oder impliziten Aufruf mittels super
wird der Konstruktor der abgeleiteten Klasse im Kontext des Objektes ausgeführt, das durch den Konstruktorenaufruf der Basisklasse erzeugt wurde, hier also des Konstruktors Array
.
Im Umkehrschluss heißt das, dass nicht einfach der Konstruktor der Basisklasse als Funktion im Kontext des Objektes aufgerufen wird, das von dem Konstruktor der abgeleiteten Klasse erzeugt wurde, wie dies etwa in der TypeScript-Übersetzung geschieht.
Wenn es also um selbstdefinierte Klassen beziehungsweise Konstruktoren geht, die plane Objekte erzeugen, dann besteht hier natürlich kein großer Unterschied, aber in Bezug auf eingebaute Objekttypen mit besonderer Semantik, wie es eben bei Arrays der Fall ist, wird hierdurch eine Funktionalität bereitgestellt, die es früher nicht gegeben hat.
Denn hierdurch wird vom Konstruktor der Basisklasse ein Objekt erzeugt, dessen Prototyp das Objekt ist, welches in der Eigenschaft prototype
der abgeleiteten Klasse hinterlegt ist. Das heißt, bezogen auf das Beispiel oben, wird ein exotisches Arrayobjekt erzeugt, dessen Prototyp CustomArray.prototype
ist, und dies kann auf andere Weise nicht erreicht werden!
function CustomArray ( ) {
const array = Array.from(arguments);
Object.setPrototypeOf(array, CustomArray.prototype);
// array.__proto__ = CustomArray.prototype;
return array;
}
CustomArray.prototype = [ ];
const instance = CustomArray( );
Sprich, wenn es um eingebaute, spezifische Objekttypen geht, kann das Einfügen eines Objektes in die Prototypenkette ohne die neue Syntax immer nur nachträglich geschehen, nachdem das entsprechende Instanzobjekt erzeugt wurde, was sich allerdings ziemlich negativ auf die Performance auswirkt.
Sofern man also Array.prototype
nicht manipulieren will, man die Methoden auch nicht auf jeder einzelnen Instanz definieren möchte, man die nachträgliche Injektion in die Prototypenkette vermeiden will und man die Funktionalität auch nicht auf andere Weise bereitzustellen gedenkt, bleibt also ganz einfach nichts anderes übrig, als sich der Syntax für abgeleitete Klassen zu bedienen. ;-)
Viele Grüße,
Orlok