Orlok: Problem mit Arrayindex

Beitrag lesen

Hallo Rolf

Array-Elemente sind aber keine Objekt-Eigenschaften.

Äh, doch.

Es sind Objekt–Eigenschaften. Allerdings gibt es hinsichtlich ihrer Definition einige Unterschiede im Vergleich zu den Eigenschaften gewöhnlicher Objekte. Darum werden Arrays im Kontext von ECMAScript auch als ‚exotische Objekte‘ bezeichnet.

Etwas genauer:

Sowohl gewöhnliche als auch exotische Objekte besitzen eine Reihe interner Methoden, die Operationen auf dem Objekt durchführen. Ein Beispiel für eine solche interne Methode wäre die Methode [[Delete]](). Wird diese Methode mit einem Eigenschaftsnamen als Argument aufgerufen, dann wird versucht die zugehörige Eigenschaft auf dem Objekt zu löschen. Ein weiteres Beispiel wäre die interne Methode [[PreventExtensions]](), die auf dem Objekt ein Flag setzt, das weitere Eigenschaftsdefinitionen verhindert.

Neue Eigenschaften werden über die interne Methode [[DefineOwnProperty]]() zu einem Objekt hinzugefügt – und die Definition dieser internen Methode unterscheidet Arrays von gewöhnlichen Objekten. Das ist nötig, damit die Eigenschaft length bei Operationen auf den Elementen des Arrays entsprechend angepasst werden kann – und umgekehrt.

Das heißt, wenn eine Eigenschaft hinzugefügt werden soll, deren Name (gegebenenfalls nach einer Coercion) einen numerischen Index darstellt, hier eine nicht negative Ganzzahl zwischen 0 und 2³² - 1, dann wird bei Arrays geprüft, ob der Wert größer oder gleich dem Wert der Objekteigenschaft length ist. In dem Fall, dass die Eigenschaftsdefinition nicht aus anderen Gründen scheitert (siehe zum Beispiel oben) und der neue Index größer gleich dem alten Wert dieser Eigenschaft ist, dann wird length auf den ermittelten Index + 1 gesetzt, nachdem die Eigenschaft für diesen Index hinzugefügt wurde.

Mal als Beispiel:

// Create array with 3 elements

const array = [1, 2, 3];

array.length; // 3


// Define new property with name '9'

array[9] = 4;

array.length; // 10

Hier wird ein Array mit drei Elementen initialisiert, oder anders ausgedrückt, mit drei Eigenschaften deren Name ein numerischer Index ist. Durch die folgende Zuweisung, deren Name ein Index größer dem Wert der Eigenschaft length ist, wird der Eigenschaftswert von length automatisch angepasst, so dass er weiterhin um 1 größer ist als der größte in dem Array verwendete Index. Hierbei ist zu beachten, dass eine solche Zuweisung nicht dazu führt, dass auch Indizes kleiner dem neu hinzugefügten Index als Eigenschaften auf dem Array definiert werden, wenn entsprechende Eigenschaften wie im obigen Beispiel zum Zeitpunkt der Zuweisung nicht existieren.

// Check if assignment created more than one property

array.hasOwnProperty(7); // false

Die Zuweisung an einen Index echt größer dem Wert der Eigenschaft length führt also dazu, dass in dem Array Lücken entstehen, es also Indizes kleiner der Länge des Arrays gibt, für die keine Eigenschaften definiert sind. Wie damit umgegangen wird, hängt dann von den jeweiligen Operationen ab, die auf dem Array ausgeführt werden. Eingebaute Funktionen wie zum Beispiel map(), reduce() oder filter() führen vor der Ausführung der als Argument übergebenen Rückruffunktionen für ein Element des Arrays eine Prüfung durch, ob für den aktuellen Index überhaupt eine Eigenschaft definiert wurde. (Das wird mit der internen Methode [[HasProperty]]() geprüft, die für alle Objekte definiert ist.) Ist keine Eigenschaft definiert, wird der Index übersprungen. Andere Methoden wie zum Beispiel die Arraymethode fill führen keine solche Prüfung durch. Hier werden nicht vorhandene Eigenschaften angelegt und mit dem gewünschten Wert befüllt.

Lücken innerhalb eines Arrays können auch entstehen, wenn die Eigenschaft length gesetzt wird, entweder explizit durch Zuweisung, oder implizit, beispielsweise als Folge eines Aufrufs von Array mit einem einzelnen Argument, das einen Index repräsentiert.

Schauen wir uns hierzu ein Beispiel an:

// That's why you should use the array initializer syntax

const array = Array(10);

array.length; // 10


// No properties defined

array.hasOwnProperty(5); // false

In dem Beispiel oben wird durch den Aufruf der Funktion Array ein neues Array erzeugt. Auf den ersten Blick mag es vielleicht so aussehen, als ob dieses Array mit einem Element, nämlich der Zahl 10 initialisiert wird, aber das ist falsch. Da 10 eine nicht negative ganze Zahl innerhalb des für Arrayindizes definierten Wertebereichs ist, wird stattdessen der Wert der Eigenschaft length des neu erzeugten Arrays auf 10 gesetzt. Eigenschaften für die Indizes kleiner als 10 werden nicht angelegt.

const array = [1, 2, 3, 4, 5];

array.length = 3;

array; // [1, 2, 3]

Wird die Eigenschaft length auf einen Wert kleiner dem aktuellen Wert gesetzt, dann sorgt die Ausführung der internen Methode [[DefineOwnProperty]] dafür, dass alle Eigenschaften gelöscht werden, deren Name ein Index größer gleich dem neuen Wert von length ist.

Gelöscht werden auch Eigenschaften, die dem delete–Operator übergeben werden. Hier unterscheiden sich Array–Eigenschaften nicht von Eigenschaften gewöhnlicher Objekte. Das bedeutet insbesondere, dass der Wert von length nicht automatisch angepasst wird, selbst wenn der Name der gelöschten Eigenschaft der größte Index des Arrays ist. Auch in diesem Fall entstehen also Lücken und beim Zugriff auf eine gelöschte Eigenschaft wird wie gewöhnlich der Wert undefined substituiert.

const array = [1, 2, 3, 4, 5];


// Remove element at index 2

array.splice(2, 1);


// Check array

array; // [1, 2, 4, 5]

array.length; // 4

Wie hier im Thread bereits gesagt wurde, sollte zum Entfernen eines Elements aus einem Array (wobei das Array selbst verändert werden soll) also besser die Methode splice verwendet werden. Hier rücken Elemente mit einem Index größer dem gelöschten Index auf, sodass etwaige Lücken geschlossen werden – und der Wert der Eigenschaft length wird entsprechend angepasst.

Viele Grüße,

Orlok