Hallo Felix
Irgendwie habe ich heute echt Probleme zu verstehen, was manche Leute mir sagen wollen. ;-)
Nehmen wir an, ich hätte für ein älteres Objekt selbst eine
forEach
-Methode gebastelt und sie überArray.prototype
an meine Arrays geflanscht.
Du meinst, für ein älteres Projekt?
Nun, da der Standard eine solche Methode vorsieht, wäre dieses Vorgehen insofern unproblematisch, als dass ich auf diese Manipulation von
Array.prototype
zugunsten der nun nativ vorhandenen Methode schlicht verzichten könnte.
Wenn die Frage lautet, ob man die native forEach
-Methode ohne Netz und doppelten Boden benutzen kann, dann lautet die Antwort: Ja. Die wird von allen relevanten Ausführungsumgebungen unterstützt.
Die Reihenfolge der Parameter für die Callback-Funktion einmal außen vor.
Nun ist es in der Tat aber so, dass ich diese Methode nicht
forEach
, sonderneach
genannt habe. Momentan stört sich das vielleicht nicht, weil andere Entwickler sich vielleicht mit dem Entwurf für ECMA-Script 6
ECMAScript 5
beschäftigt haben, und ein eventuell vorausgreifendes Polyfill schon mit dem richtigen Bezeichner
forEach
erstellt haben - und meineach
niemanden (mehr) juckt oder jucken wird.
Inwiefern deine Methode each
tatsächlich jemanden juckt oder jucken wird, vermag ich natürlich nicht zu sagen, aber prinzipiell wäre das Vorgehen schon problematisch, denn wie du ja selbst sagst, handelt es sich bei deiner Methode nicht um ein standardkonformes Polyfill.
Für die Zukunft
Wie baue ich mir ein eigenes Array (nennen wir es MyArray), das ebenso prototypisch von Array erbt, Änderungen an
MyArray.prototype
aber nur dort, nicht aber anArray.prototype
vorgenommen werden?
Sorry, irgendwie stehe ich etwas auf dem Schlauch, aber wenn ich dich richtig verstehe, dann willst du in etwa sowas wie das hier …
function MyArray ( ) {
// create array
}
MyArray.prototype.method = function ( ) {
// do something
};
const array = new MyArray;
array.method( );
… wobei der Prototyp von MyArray.prototype
das Objekt Array.prototype
sein soll, damit die erzeugten Instanzen sowohl die selbstdefinierten Methoden als auch die eingebauten Methoden erben, ohne dass dabei Array.prototype
angefasst werden muss. Richtig?
Wenn das deine Frage sein sollte, dann habe ich dazu im Abschnitt Selbstdefinierte Methoden meines Artikels ja schon einiges geschrieben. Aber ich kann gerne noch einige Punkte ergänzen, die dort vielleicht etwas zu kurz gekommen sind.
Zum Beispiel könnte man erklären, warum die oben gezeigte Variante mit dem Konstruktor unter den genannten Voraussetzungen nicht funktionieren kann.
MyArray.prototype = [ ];
Dabei wäre zunächst zu erwähnen, dass es natürlich möglich ist, ein Array als prototypisches Objekt des Konstruktors anzulegen, sodass die von dieser Funktion erzeugten Instanzen sowohl die auf diesem Objekt definierten eigenen Methoden, als auch die Methoden von Array.prototype
erben.
const instance = new MyArray;
console.log(Array.prototype.isPrototypeOf(instance)); // true
console.log(Array.isArray(instance)); // false
Aber das Objekt, das bei einem Aufruf via [[Construct]] erzeugt wird, in dessen Kontext die Funktion ausgeführt wird und das standardmäßig deren Rückgabewert ist, ist eben kein Array, sondern ein gewöhnliches Objekt.
Das heißt, einem auf diese Weise erzeugten Objekt fehlt die besondere Semantik von Arrays, es besitzt also weder eine Eigenschaft length
, noch das besondere Verhalten beim schreibenden Zugriff auf Objekteigenschaften, wie es in der internen Methode [[DefineOwnProperty]] bestimmt ist.
function MyArray ( ) {
return Array.from(arguments);
}
const instance = new MyArray;
console.log(MyArray.prototype.isPrototypeOf(instance)); // false
Wird in der Konstruktorfunktion hingegen ein anderes Objekt als Rückgabewert bestimmt, als das Objekt in dessen Kontext die Funktion ausgeführt wird, dann wird zwar dieses Objekt zurückgegeben, aber es wird dabei nicht der Prototyp verändert. Das heißt, man bekäme hierbei nur ein normales Array, ohne dass dessen Prototyp auf MyArray.prototype
gesetzt würde.
Es ist also egal wie man es dreht und wendet, man bekommt hier entweder nur ein ganz gewöhnliches Array, dessen interne Eigenschaft [[Prototype]] eine Referenz auf das Objekt Array.prototype
enthält, oder aber ein Objekt, das zwar über die gewünschte Prototypenkette verfügt, das aber selbst kein exotisches Arrayobjekt ist.
Wenn wir nun aus guten Gründen die Möglichkeit ignorieren, nach der Erzeugung eines Arrays ein Objekt in dessen Prototypenkette einzufügen, dann kann die gewünschte Funktionalität tatsächlich nur auf eine Weise implementiert werden, nämlich in Form einer von Array abgeleiteten Klasse.
class MyArray extends Array {
method ( ) {
// do something
}
}
const instance = new MyArray;
console.log(Array.isArray(instance)); // true
Hier haben wir also eine Klasse MyArray
, die vom Konstruktor Array
abgeleitet ist. Das heißt, die von dieser Klasse erzeugten Instanzen sind richtige Arrays, die sowohl die Methoden von MyArray.prototype
als auch die Methoden von Array.prototype
erben.
console.log(MyArray.prototype.hasOwnProperty('method')); // true
console.log(MyArray.prototype.propertyIsEnumerable('method')); // false
Die im Körper der Klasse definierten Methoden werden dabei automatisch auf dem prototypischen Objekt der Funktion angelegt. Diese Form der Methodendefinition hat darüber hinaus den Vorteil, dass das interne Attribut [[Enumerable]] der Methode standardmäßig auf false
gesetzt wird.
class MyArray extends Array {
constructor ( ) {
super(...arguments);
console.log(this.length);
}
method ( ) {
// do something
}
}
const array = new MyArray(2, 4, 8); // 3
Optional kann innerhalb der Klasse die Pseudomethode constructor
notiert werden, die im Prinzip wie ein gewöhnlicher Konstruktor funktioniert. Damit die Methode bei einer abgeleiteten Klasse aber auch im Kontext des Objektes ausgeführt wird, welches von der Superklasse erzeugt wird, hier also durch den Konstruktor Array
, muss diese innerhalb der Methode mit dem Keyword super
aufgerufen werden.
Im Ergebnis ist hier also Array.prototype
der Prototyp von MyArray.prototype
und die erzeugten Instanzen sind native Arrays. Das heißt es können, vorzugsweise aber nicht zwingend innerhalb des Körpers der Klasse, beliebige Arraymethoden definiert werden, ohne dabei Array.prototype
zu manipulieren.
Für die Zunkunft ist das also das Mittel der Wahl, wenn es darum geht, eigene Methoden für Arrays zu definieren.
Viele Grüße,
Orlok