Array)for-of und Arrays [ES6]for-of: über Elemente iterierenfor-of: über Indizes iterierenfor-of: über [Index, Element]-Paare iterieren.find(), .map(), .filter(), etc.).find(), .findIndex().map(): Kopieren und dabei Elemente neue Werte geben.flatMap(): zu null oder mehr Werten mappen.filter(): nur einige der Elemente behalten.reduce(): einen Wert aus einem Array ableiten (Fortgeschrittene).sort(): Arrays sortierenArraynew Array()ArrayArray.prototypeJavaScript Arrays sind eine sehr flexible Datenstruktur und werden als Listen, Stacks, Queues, Tupel (z. B. Paare) und mehr verwendet.
Einige Array-bezogene Operationen ändern Arrays destruktiv. Andere erzeugen nicht-destruktiv neue Arrays mit den Änderungen, die auf eine Kopie des ursprünglichen Inhalts angewendet werden.
Ein Array erstellen, Elemente lesen und schreiben
// Creating an Array
const arr = ['a', 'b', 'c']; // Array literal
assert.deepEqual(
arr,
[ // Array literal
'a',
'b',
'c', // trailing commas are ignored
]
);
// Reading elements
assert.equal(
arr[0], 'a' // negative indices don’t work
);
assert.equal(
arr.at(-1), 'c' // negative indices work
);
// Writing an element
arr[0] = 'x';
assert.deepEqual(
arr, ['x', 'b', 'c']
);Die Länge eines Arrays
const arr = ['a', 'b', 'c'];
assert.equal(
arr.length, 3 // number of elements
);
arr.length = 1; // removing elements
assert.deepEqual(
arr, ['a']
);
arr[arr.length] = 'b'; // adding an element
assert.deepEqual(
arr, ['a', 'b']
);Elemente destruktiv per .push() hinzufügen
const arr = ['a', 'b'];
arr.push('c'); // adding an element
assert.deepEqual(
arr, ['a', 'b', 'c']
);
// Pushing Arrays (used as arguments via spreading (...)):
arr.push(...['d', 'e']);
assert.deepEqual(
arr, ['a', 'b', 'c', 'd', 'e']
);Elemente nicht-destruktiv per Spread-Syntax (...) hinzufügen
const arr1 = ['a', 'b'];
const arr2 = ['c'];
assert.deepEqual(
[...arr1, ...arr2, 'd', 'e'],
['a', 'b', 'c', 'd', 'e']
);Arrays leeren (alle Elemente entfernen)
// Destructive – affects everyone referring to the Array:
const arr1 = ['a', 'b', 'c'];
arr1.length = 0;
assert.deepEqual(
arr1, []
);
// Non-destructive – does not affect others referring to the Array:
let arr2 = ['a', 'b', 'c'];
arr2 = [];
assert.deepEqual(
arr2, []
);Über Elemente iterieren
const arr = ['a', 'b', 'c'];
for (const value of arr) {
console.log(value);
}
// Output:
// 'a'
// 'b'
// 'c'Über Index-Wert-Paare iterieren
const arr = ['a', 'b', 'c'];
for (const [index, value] of arr.entries()) {
console.log(index, value);
}
// Output:
// 0, 'a'
// 1, 'b'
// 2, 'c'Arrays erstellen und füllen, wenn wir keine Array-Literale verwenden können (z. B. weil wir ihre Längen im Voraus nicht kennen oder sie zu groß sind)
const four = 4;
// Empty Array that we’ll fill later
assert.deepEqual(
new Array(four),
[ , , , ,] // four holes; last comma is ignored
);
// An Array filled with a primitive value
assert.deepEqual(
new Array(four).fill(0),
[0, 0, 0, 0]
);
// An Array filled with objects
// Why not .fill()? We’d get single object, shared multiple times.
assert.deepEqual(
Array.from({length: four}, () => ({})),
[{}, {}, {}, {}]
);
// A range of integers
assert.deepEqual(
Array.from({length: four}, (_, i) => i),
[0, 1, 2, 3]
);Dieser Abschnitt gibt einen kurzen Überblick über die Array-API. Am Ende dieses Kapitels gibt es eine umfassendere Schnellreferenz.
Ein neues Array aus einem bestehenden Array ableiten
> ['■','●','▲'].slice(1, 3)
['●','▲']
> ['■','●','■'].filter(x => x==='■')
['■','■']
> ['▲','●'].map(x => x+x)
['▲▲','●●']
> ['▲','●'].flatMap(x => [x,x])
['▲','▲','●','●']Ein Array-Element an einem gegebenen Index entfernen
// .filter(): remove non-destructively
const arr1 = ['■','●','▲'];
assert.deepEqual(
arr1.filter((_, index) => index !== 1),
['■','▲']
);
assert.deepEqual(
arr1, ['■','●','▲'] // unchanged
);
// .splice(): remove destructively
const arr2 = ['■','●','▲'];
arr2.splice(1, 1); // start at 1, delete 1 element
assert.deepEqual(
arr2, ['■','▲'] // changed
);Eine Zusammenfassung eines Arrays berechnen
> ['■','●','▲'].some(x => x==='●')
true
> ['■','●','▲'].every(x => x==='●')
false
> ['■','●','▲'].join('-')
'■-●-▲'
> ['■','▲'].reduce((result,x) => result+x, '●')
'●■▲'
> ['■','▲'].reduceRight((result,x) => result+x, '●')
'●▲■'Umkehren und Füllen
// .reverse() changes and returns `arr`
const arr = ['■','●','▲'];
assert.deepEqual(
arr.reverse(), arr
);
// `arr` was changed:
assert.deepEqual(
arr, ['▲','●','■']
);
// .fill() works the same way:
assert.deepEqual(
['■','●','▲'].fill('●'),
['●','●','●']
);.sort() modifiziert ebenfalls ein Array und gibt es zurück
// By default, string representations of the Array elements
// are sorted lexicographically:
assert.deepEqual(
[200, 3, 10].sort(),
[10, 200, 3]
);
// Sorting can be customized via a callback:
assert.deepEqual(
[200, 3, 10].sort((a,b) => a - b), // sort numerically
[ 3, 10, 200 ]
);Array-Elemente finden
> ['■','●','■'].includes('■')
true
> ['■','●','■'].indexOf('■')
0
> ['■','●','■'].lastIndexOf('■')
2
> ['■','●','■'].find(x => x==='■')
'■'
> ['■','●','■'].findIndex(x => x==='■')
0Elemente am Anfang oder Ende hinzufügen oder entfernen
// Adding and removing at the start
const arr1 = ['■','●'];
arr1.unshift('▲');
assert.deepEqual(
arr1, ['▲','■','●']
);
arr1.shift();
assert.deepEqual(
arr1, ['■','●']
);
// Adding and removing at the end
const arr2 = ['■','●'];
arr2.push('▲');
assert.deepEqual(
arr2, ['■','●','▲']
);
arr2.pop();
assert.deepEqual(
arr2, ['■','●']
);Es gibt zwei Arten, Arrays in JavaScript zu verwenden:
In der Praxis werden diese beiden Arten oft gemischt.
Insbesondere sind Sequenz-Arrays so flexibel, dass wir sie als (traditionelle) Arrays, Stacks und Queues verwenden können. Wie das geht, sehen wir später.
Der beste Weg, ein Array zu erstellen, ist über ein Array-Literal
const arr = ['a', 'b', 'c'];Das Array-Literal beginnt und endet mit eckigen Klammern []. Es erstellt ein Array mit drei Elementen: 'a', 'b' und 'c'.
Nachgestellte Kommas sind in Array-Literalen erlaubt und werden ignoriert.
const arr = [
'a',
'b',
'c',
];Um ein Array-Element zu lesen, setzen wir einen Index in eckige Klammern (Indizes beginnen bei Null).
const arr = ['a', 'b', 'c'];
assert.equal(arr[0], 'a');Um ein Array-Element zu ändern, weisen wir einem Array mit einem Index einen Wert zu.
const arr = ['a', 'b', 'c'];
arr[0] = 'x';
assert.deepEqual(arr, ['x', 'b', 'c']);Der Bereich der Array-Indizes ist 32 Bit (ohne die maximale Länge): [0, 232−1).
.length-Eigenschaft eines ArraysJedes Array hat eine Eigenschaft .length, die sowohl die Anzahl der Elemente in einem Array liest als auch (!) ändert.
Die Länge eines Arrays ist immer der höchste Index plus eins.
> const arr = ['a', 'b'];
> arr.length
2Wenn wir in das Array am Index der Länge schreiben, hängen wir ein Element an.
> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3Eine weitere Möglichkeit, ein Element anzuhängen (destruktiv), ist über die Array-Methode .push().
> arr.push('d');
> arr
[ 'a', 'b', 'c', 'd' ]Wenn wir .length setzen, kürzen wir das Array, indem wir Elemente entfernen.
> arr.length = 1;
> arr
[ 'a' ] Übung: Leere Zeilen mit
.push() entfernen
exercises/arrays/remove_empty_lines_push_test.mjs
Mehrere Array-Methoden unterstützen negative Indizes. Ist ein Index negativ, wird er zur Länge eines Arrays addiert, um einen nutzbaren Index zu erhalten. Daher sind die folgenden beiden Aufrufe von .slice() äquivalent: Sie kopieren beide arr ab dem letzten Element.
> const arr = ['a', 'b', 'c'];
> arr.slice(-1)
[ 'c' ]
> arr.slice(arr.length - 1)
[ 'c' ].at(): einzelne Elemente lesen (unterstützt negative Indizes) [ES2022]Die Array-Methode .at() gibt das Element an einem gegebenen Index zurück. Sie unterstützt positive und negative Indizes (-1 bezieht sich auf das letzte Element, -2 auf das vorletzte Element usw.).
> ['a', 'b', 'c'].at(0)
'a'
> ['a', 'b', 'c'].at(-1)
'c'Im Gegensatz dazu unterstützt der Klammeroperator [] keine negativen Indizes (und kann nicht geändert werden, da dies bestehenden Code brechen würde). Er interpretiert sie als Schlüssel von Nicht-Element-Properties.
const arr = ['a', 'b', 'c'];
arr[-1] = 'non-element property';
// The Array elements didn’t change:
assert.deepEqual(
Array.from(arr), // copy just the Array elements
['a', 'b', 'c']
);
assert.equal(
arr[-1], 'non-element property'
);Um ein Array zu leeren (zu entleeren), können wir entweder seine .length auf null setzen
const arr = ['a', 'b', 'c'];
arr.length = 0;
assert.deepEqual(arr, []);oder wir können der Variablen, die das Array speichert, ein neues leeres Array zuweisen.
let arr = ['a', 'b', 'c'];
arr = [];
assert.deepEqual(arr, []);Der letztere Ansatz hat den Vorteil, dass er keine anderen Orte beeinflusst, die auf dasselbe Array zeigen. Wenn wir jedoch ein gemeinsam genutztes Array für alle zurücksetzen wollen, dann brauchen wir den ersteren Ansatz.
Innerhalb eines Array-Literals besteht ein Spread-Element aus drei Punkten (...) gefolgt von einem Ausdruck. Es ergibt sich, dass der Ausdruck ausgewertet und dann iteriert wird. Jeder iterierte Wert wird zu einem zusätzlichen Array-Element – zum Beispiel:
> const iterable = ['b', 'c'];
> ['a', ...iterable, 'd']
[ 'a', 'b', 'c', 'd' ]Das bedeutet, dass wir mit Spread-Syntax eine Kopie eines Arrays erstellen und ein Iterable in ein Array umwandeln können.
const original = ['a', 'b', 'c'];
const copy = [...original];
const iterable = original.keys();
assert.deepEqual(
[...iterable], [0, 1, 2]
);Für beide vorherigen Anwendungsfälle finde ich Array.from() jedoch aussagekräftiger und bevorzuge es.
const copy2 = Array.from(original);
assert.deepEqual(
Array.from(original.keys()), [0, 1, 2]
);Spread-Syntax ist auch praktisch zum Verketten von Arrays (und anderen Iterables) zu Arrays.
const arr1 = ['a', 'b'];
const arr2 = ['c', 'd'];
const concatenated = [...arr1, ...arr2, 'e'];
assert.deepEqual(
concatenated,
['a', 'b', 'c', 'd', 'e']);Da Spread-Syntax Iteration verwendet, funktioniert sie nur, wenn der Wert iterierbar ist.
> [...'abc'] // strings are iterable
[ 'a', 'b', 'c' ]
> [...123]
TypeError: 123 is not iterable
> [...undefined]
TypeError: undefined is not iterable Spread-Syntax und
Array.from() erzeugen flache Kopien
Das Kopieren von Arrays per Spread-Syntax oder per Array.from() ist flach: Wir erhalten neue Einträge in einem neuen Array, aber die Werte werden mit dem ursprünglichen Array geteilt. Die Folgen einer flachen Kopie werden in §28.4 „Spread-Syntax in Objekt-Literalen (...) [ES2018]“ gezeigt.
Die Methode .keys() listet die Indizes eines Arrays auf.
const arr = ['a', 'b'];
assert.deepEqual(
Array.from(arr.keys()), // (A)
[0, 1]);.keys() gibt ein Iterable zurück. In Zeile A wandeln wir dieses Iterable in ein Array um.
Das Auflisten von Array-Indizes unterscheidet sich vom Auflisten von Properties. Ersteres erzeugt Zahlen; letzteres erzeugt (zusätzlich zu Nicht-Index-Property-Keys) String-ifizierte Zahlen.
const arr = ['a', 'b'];
arr.prop = true;
assert.deepEqual(
Object.keys(arr),
['0', '1', 'prop']);Die Methode .entries() listet den Inhalt eines Arrays als [Index, Element]-Paare auf.
const arr = ['a', 'b'];
assert.deepEqual(
Array.from(arr.entries()),
[[0, 'a'], [1, 'b']]);Folgend sind zwei Möglichkeiten, zu überprüfen, ob ein Wert ein Array ist:
> [] instanceof Array
true
> Array.isArray([])
trueinstanceof ist normalerweise in Ordnung. Wir benötigen Array.isArray(), wenn ein Wert aus einem anderen Realm stammen kann. Grob gesagt ist ein Realm eine Instanz des globalen Geltungsbereichs von JavaScript. Einige Realms sind voneinander isoliert (z. B. Web Workers in Browsern), aber es gibt auch Realms, zwischen denen wir Daten bewegen können – zum Beispiel Same-Origin-Iframes in Browsern. x instanceof Array prüft die Prototypenkette von x und gibt daher false zurück, wenn x ein Array aus einem anderen Realm ist.
typeof kategorisiert Arrays als Objekte.
> typeof []
'object'for-of und Arrays [ES6]Die for-of-Schleife haben wir bereits früher in diesem Buch behandelt. Dieser Abschnitt fasst kurz zusammen, wie sie für Arrays verwendet wird.
for-of: über Elemente iterierenDie folgende for-of-Schleife iteriert über die Elemente eines Arrays.
for (const element of ['a', 'b']) {
console.log(element);
}
// Output:
// 'a'
// 'b'for-of: über Indizes iterierenDiese for-of-Schleife iteriert über die Indizes eines Arrays.
for (const element of ['a', 'b'].keys()) {
console.log(element);
}
// Output:
// 0
// 1for-of: über [Index, Element]-Paare iterierenDie folgende for-of-Schleife iteriert über [Index, Element]-Paare. Destrukturierung (später beschrieben) gibt uns eine bequeme Syntax für die Einrichtung von index und element im Kopf von for-of.
for (const [index, element] of ['a', 'b'].entries()) {
console.log(index, element);
}
// Output:
// 0, 'a'
// 1, 'b'Manche Operationen, die mit Arrays funktionieren, benötigen nur das absolute Minimum: Werte müssen nur Array-ähnlich sein. Ein Array-ähnlicher Wert ist ein Objekt mit den folgenden Eigenschaften:
.length: enthält die Länge des Array-ähnlichen Objekts.[0]: enthält das Element am Index 0 (und so weiter). Beachten Sie, dass Zahlen als Property-Namen verwendet werden, sie werden immer zu Strings konvertiert. Daher ruft [0] den Wert der Property ab, deren Schlüssel '0' ist.Zum Beispiel akzeptiert Array.from() Array-ähnliche Objekte und wandelt sie in Arrays um.
// If we omit .length, it is interpreted as 0
assert.deepEqual(
Array.from({}),
[]);
assert.deepEqual(
Array.from({length:2, 0:'a', 1:'b'}),
[ 'a', 'b' ]);Die TypeScript-Schnittstelle für Array-ähnliche Objekte ist:
interface ArrayLike<T> {
length: number;
[n: number]: T;
} Array-ähnliche Objekte sind in modernem JavaScript relativ selten
Array-ähnliche Objekte waren vor ES6 üblich; heute sehen wir sie nicht mehr sehr oft.
Es gibt zwei gängige Methoden, um Iterables und Array-ähnliche Werte in Arrays umzuwandeln:
Array.from()Ich bevorzuge letzteres – ich finde es selbsterklärender.
...) in Arrays umwandelnInnerhalb eines Array-Literals wandelt Spread-Syntax per ... jedes iterable Objekt in eine Serie von Array-Elementen um. Zum Beispiel:
// Get an Array-like collection from a web browser’s DOM
const domCollection = document.querySelectorAll('a');
// Alas, the collection is missing many Array methods
assert.equal('map' in domCollection, false);
// Solution: convert it to an Array
const arr = [...domCollection];
assert.deepEqual(
arr.map(x => x.href),
['https://2ality.com', 'https://exploringjs.de']);Die Umwandlung funktioniert, da die DOM-Kollektion iterierbar ist.
Array.from() in Arrays umwandelnArray.from() kann in zwei Modi verwendet werden.
Array.from(): UmwandlungDer erste Modus hat die folgende Typsignatur:
.from<T>(iterable: Iterable<T> | ArrayLike<T>): T[]Die Schnittstelle Iterable ist im Kapitel über synchrone Iteration gezeigt. Die Schnittstelle ArrayLike erschien früher in diesem Kapitel.
Mit einem einzigen Parameter wandelt Array.from() alles, was iterierbar oder Array-ähnlich ist, in ein Array um.
> Array.from(new Set(['a', 'b']))
[ 'a', 'b' ]
> Array.from({length: 2, 0:'a', 1:'b'})
[ 'a', 'b' ]Array.from(): Umwandlung und MappingDer zweite Modus von Array.from() umfasst zwei Parameter:
.from<T, U>(
iterable: Iterable<T> | ArrayLike<T>,
mapFunc: (v: T, i: number) => U,
thisArg?: any)
: U[]In diesem Modus macht Array.from() mehrere Dinge:
iterable.mapFunc mit jedem iterierten Wert auf. Der optionale Parameter thisArg gibt ein this für mapFunc an.mapFunc auf jeden iterierten Wert an.Anders ausgedrückt: Wir gehen von einem Iterable mit Elementen vom Typ T zu einem Array mit Elementen vom Typ U.
Dies ist ein Beispiel:
> Array.from(new Set(['a', 'b']), x => x + x)
[ 'aa', 'bb' ]Der beste Weg, ein Array zu erstellen, ist über ein Array-Literal. Wir können es jedoch nicht immer verwenden: Das Array ist möglicherweise zu groß, wir kennen seine Länge während der Entwicklung möglicherweise nicht, oder wir möchten seine Länge flexibel halten. Dann empfehle ich die folgenden Techniken zum Erstellen und möglicherweise Füllen von Arrays.
> new Array(3)
[ , , ,]Beachten Sie, dass das Ergebnis drei Löcher (leere Slots) hat – das letzte Komma in einem Array-Literal wird immer ignoriert.
> new Array(3).fill(0)
[0, 0, 0]Hinweis: Wenn wir .fill() mit einem Objekt verwenden, verweist jedes Array-Element auf dieses Objekt (teilt es).
const arr = new Array(3).fill({});
arr[0].prop = true;
assert.deepEqual(
arr, [
{prop: true},
{prop: true},
{prop: true},
]);Der nächste Unterabschnitt erklärt, wie dies behoben werden kann.
> new Array(3).fill(0)
[0, 0, 0]Bei großen Größen kann das temporäre Array ziemlich viel Speicher verbrauchen. Der folgende Ansatz hat diesen Nachteil nicht, ist aber weniger selbsterklärend:
> Array.from({length: 3}, () => ({}))
[{}, {}, {}]Anstelle eines temporären Arrays verwenden wir ein temporäres Array-ähnliches Objekt.
function createRange(start, end) {
return Array.from({length: end-start}, (_, i) => i+start);
}
assert.deepEqual(
createRange(2, 5),
[2, 3, 4]);Hier ist eine alternative, leicht hackige Technik zum Erstellen von Zahlenreihen, die bei Null beginnen:
/** Returns an iterable */
function createRange(end) {
return new Array(end).keys();
}
assert.deepEqual(
Array.from(createRange(4)),
[0, 1, 2, 3]);Das funktioniert, weil .keys() Löcher wie undefined-Elemente behandelt und ihre Indizes auflistet.
Wenn wir mit Arrays von ganzen Zahlen oder Fließkommazahlen arbeiten, sollten wir Typed Arrays in Betracht ziehen, die für diesen Zweck entwickelt wurden.
JavaScript hat keine echten mehrdimensionalen Arrays; wir müssen auf Arrays zurückgreifen, deren Elemente Arrays sind.
function initMultiArray(...dimensions) {
function initMultiArrayRec(dimIndex) {
if (dimIndex >= dimensions.length) {
return 0;
} else {
const dim = dimensions[dimIndex];
const arr = [];
for (let i=0; i<dim; i++) {
arr.push(initMultiArrayRec(dimIndex+1));
}
return arr;
}
}
return initMultiArrayRec(0);
}
const arr = initMultiArray(4, 3, 2);
arr[3][2][1] = 'X'; // last in each dimension
assert.deepEqual(arr, [
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 'X' ] ],
]);In diesem Abschnitt betrachten wir Phänomene, die uns bei der Arbeit mit Arrays nicht oft begegnen.
Man könnte denken, dass Array-Elemente etwas Besonderes sind, weil wir sie über Zahlen ansprechen. Aber der Klammeroperator [] dafür ist derselbe Operator, der für den Zugriff auf Properties verwendet wird. Er konvertiert jeden Wert (der kein Symbol ist) in einen String. Daher sind Array-Elemente (fast) normale Properties (Zeile A), und es spielt keine Rolle, ob wir Zahlen oder Strings als Indizes verwenden (Zeilen B und C).
const arr = ['a', 'b'];
arr.prop = 123;
assert.deepEqual(
Object.keys(arr),
['0', '1', 'prop']); // (A)
assert.equal(arr[0], 'a'); // (B)
assert.equal(arr['0'], 'a'); // (C)Um die Sache noch verwirrender zu machen, ist dies nur, wie die Sprachspezifikation Dinge definiert (die Theorie von JavaScript, wenn Sie so wollen). Die meisten JavaScript-Engines optimieren intern und verwenden tatsächlich ganze Zahlen, um auf Array-Elemente zuzugreifen (die Praxis von JavaScript, wenn Sie so wollen).
Property-Keys (Strings!), die für Array-Elemente verwendet werden, werden als Indizes bezeichnet. Ein String str ist ein Index, wenn seine Umwandlung in eine 32-Bit vorzeichenlose Ganzzahl und zurück zum ursprünglichen Wert führt. Geschrieben als Formel:
ToString(ToUint32(str)) === str
Beim Auflisten von Property-Keys werden Indizes speziell behandelt – sie kommen immer zuerst und werden wie Zahlen sortiert ('2' kommt vor '10').
const arr = [];
arr.prop = true;
arr[1] = 'b';
arr[0] = 'a';
assert.deepEqual(
Object.keys(arr),
['0', '1', 'prop']);Beachten Sie, dass .length, .entries() und .keys() Array-Indizes als Zahlen behandeln und Nicht-Index-Properties ignorieren.
assert.equal(arr.length, 2);
assert.deepEqual(
Array.from(arr.keys()), [0, 1]);
assert.deepEqual(
Array.from(arr.entries()), [[0, 'a'], [1, 'b']]);Wir haben Array.from() verwendet, um die von .keys() und .entries() zurückgegebenen Iterables in Arrays umzuwandeln.
Wir unterscheiden zwei Arten von Arrays in JavaScript:
arr ist dicht, wenn alle Indizes i mit 0 ≤ i < arr.length existieren. Das heißt, die Indizes bilden einen zusammenhängenden Bereich.Arrays können in JavaScript dünn besetzt sein, weil Arrays eigentlich Dictionaries von Indizes zu Werten sind.
Empfehlung: Löcher vermeiden
Bisher haben wir nur dichte Arrays gesehen, und es wird tatsächlich empfohlen, Löcher zu vermeiden: Sie machen unseren Code komplizierter und werden von Array-Methoden nicht konsistent behandelt. Außerdem optimieren JavaScript-Engines dichte Arrays, was sie schneller macht.
Wir können Löcher erstellen, indem wir Indizes beim Zuweisen von Elementen überspringen.
const arr = [];
arr[0] = 'a';
arr[2] = 'c';
assert.deepEqual(Object.keys(arr), ['0', '2']); // (A)
assert.equal(0 in arr, true); // element
assert.equal(1 in arr, false); // holeIn Zeile A verwenden wir Object.keys(), weil arr.keys() Löcher behandelt, als wären sie undefined-Elemente, und sie nicht preisgibt.
Eine weitere Möglichkeit, Löcher zu erstellen, ist das Überspringen von Elementen in Array-Literalen:
const arr = ['a', , 'c'];
assert.deepEqual(Object.keys(arr), ['0', '2']);Wir können auch Array-Elemente löschen:
const arr = ['a', 'b', 'c'];
assert.deepEqual(Object.keys(arr), ['0', '1', '2']);
delete arr[1];
assert.deepEqual(Object.keys(arr), ['0', '2']);Leider gibt es viele verschiedene Arten, wie Array-Operationen Löcher behandeln.
Einige Array-Operationen entfernen Löcher.
> ['a',,'b'].filter(x => true)
[ 'a', 'b' ]Einige Array-Operationen ignorieren Löcher.
> ['a', ,'a'].every(x => x === 'a')
trueEinige Array-Operationen ignorieren, aber behalten Löcher.
> ['a',,'b'].map(x => 'c')
[ 'c', , 'c' ]Einige Array-Operationen behandeln Löcher als undefined-Elemente.
> Array.from(['a',,'b'], x => x)
[ 'a', undefined, 'b' ]
> Array.from(['a',,'b'].entries())
[[0, 'a'], [1, undefined], [2, 'b']]Object.keys() verhält sich anders als .keys() (Strings vs. Zahlen, Löcher haben keine Schlüssel).
> Array.from(['a',,'b'].keys())
[ 0, 1, 2 ]
> Object.keys(['a',,'b'])
[ '0', '2' ]Es gibt keine Regel, die man sich hier merken müsste. Wenn es jemals darauf ankommt, wie eine Array-Operation Löcher behandelt, ist der beste Ansatz, einen schnellen Test in einer Konsole durchzuführen.
JavaScript's Array ist ziemlich flexibel und ähnelt eher einer Kombination aus Array, Stack und Queue. Dieser Abschnitt untersucht Möglichkeiten, Array-Elemente hinzuzufügen und zu entfernen. Die meisten Operationen können sowohl destruktiv (das Array modifizieren) als auch nicht-destruktiv (eine modifizierte Kopie erzeugen) durchgeführt werden.
Im folgenden Code stellen wir destruktiv einzelne Elemente voran arr1 und ein Array voran arr2:
const arr1 = ['a', 'b'];
arr1.unshift('x', 'y'); // prepend single elements
assert.deepEqual(arr1, ['x', 'y', 'a', 'b']);
const arr2 = ['a', 'b'];
arr2.unshift(...['x', 'y']); // prepend Array
assert.deepEqual(arr2, ['x', 'y', 'a', 'b']);Spread-Syntax erlaubt uns, ein Array in arr2 zu "unshiften".
Nicht-destruktives Voranstellen erfolgt per Spread-Elemente:
const arr1 = ['a', 'b'];
assert.deepEqual(
['x', 'y', ...arr1], // prepend single elements
['x', 'y', 'a', 'b']);
assert.deepEqual(arr1, ['a', 'b']); // unchanged!
const arr2 = ['a', 'b'];
assert.deepEqual(
[...['x', 'y'], ...arr2], // prepend Array
['x', 'y', 'a', 'b']);
assert.deepEqual(arr2, ['a', 'b']); // unchanged!Im folgenden Code hängen wir destruktiv einzelne Elemente an arr1 und ein Array an arr2 an:
const arr1 = ['a', 'b'];
arr1.push('x', 'y'); // append single elements
assert.deepEqual(arr1, ['a', 'b', 'x', 'y']);
const arr2 = ['a', 'b'];
arr2.push(...['x', 'y']); // (A) append Array
assert.deepEqual(arr2, ['a', 'b', 'x', 'y']);Spread-Syntax (...) erlaubt uns, ein Array in arr2 zu "pushen" (Zeile A).
Nicht-destruktives Anhängen erfolgt per Spread-Elemente:
const arr1 = ['a', 'b'];
assert.deepEqual(
[...arr1, 'x', 'y'], // append single elements
['a', 'b', 'x', 'y']);
assert.deepEqual(arr1, ['a', 'b']); // unchanged!
const arr2 = ['a', 'b'];
assert.deepEqual(
[...arr2, ...['x', 'y']], // append Array
['a', 'b', 'x', 'y']);
assert.deepEqual(arr2, ['a', 'b']); // unchanged!Dies sind drei destruktive Möglichkeiten, Array-Elemente zu entfernen:
// Destructively remove first element:
const arr1 = ['a', 'b', 'c'];
assert.equal(arr1.shift(), 'a');
assert.deepEqual(arr1, ['b', 'c']);
// Destructively remove last element:
const arr2 = ['a', 'b', 'c'];
assert.equal(arr2.pop(), 'c');
assert.deepEqual(arr2, ['a', 'b']);
// Remove one or more elements anywhere:
const arr3 = ['a', 'b', 'c', 'd'];
assert.deepEqual(arr3.splice(1, 2), ['b', 'c']);
assert.deepEqual(arr3, ['a', 'd']);.splice() wird im Detail in der Schnellreferenz am Ende dieses Kapitels behandelt.
Destrukturierung per Rest-Element erlaubt uns, nicht-destruktiv Elemente vom Anfang eines Arrays zu entfernen (Destrukturierung wird später behandelt).
const arr1 = ['a', 'b', 'c'];
// Ignore first element, extract remaining elements
const [, ...arr2] = arr1;
assert.deepEqual(arr2, ['b', 'c']);
assert.deepEqual(arr1, ['a', 'b', 'c']); // unchanged!Leider muss ein Rest-Element am Ende eines Arrays stehen. Daher können wir es nur verwenden, um Suffixe zu extrahieren.
Übung: Eine Queue per Array implementieren
exercises/arrays/queue_via_array_test.mjs
.find(), .map(), .filter(), etc.)In diesem Abschnitt betrachten wir Array-Methoden zum Iterieren über Arrays und zum Transformieren von Arrays.
Alle Iterations- und Transformationsmethoden verwenden Callbacks. Erstere füttern alle iterierten Werte an ihre Callbacks; letztere fragen ihre Callbacks, wie Arrays zu transformieren sind.
Diese Callbacks haben Typsignaturen, die wie folgt aussehen:
callback: (value: T, index: number, array: Array<T>) => booleanDas heißt, der Callback erhält drei Parameter (es steht ihm frei, einen davon zu ignorieren):
value ist der wichtigste. Dieser Parameter enthält den aktuell verarbeiteten iterierten Wert.index kann dem Callback zusätzlich den Index des iterierten Werts mitteilen.array verweist auf das aktuelle Array (den Empfänger des Methodenaufrufs). Manche Algorithmen müssen auf das gesamte Array verweisen – z. B. um es nach Antworten zu durchsuchen. Dieser Parameter erlaubt uns, wiederverwendbare Callbacks für solche Algorithmen zu schreiben.Was der Callback zurückgeben soll, hängt von der Methode ab, an die er übergeben wird. Möglichkeiten sind:
.map() füllt sein Ergebnis mit den Werten, die von seinem Callback zurückgegeben werden.
> ['a', 'b', 'c'].map(x => x + x)
[ 'aa', 'bb', 'cc' ].find() gibt das erste Array-Element zurück, für das sein Callback true zurückgibt.
> ['a', 'bb', 'ccc'].find(str => str.length >= 2)
'bb'Beide Methoden werden später genauer beschrieben.
.find(), .findIndex().find() gibt das erste Element zurück, für das sein Callback einen wahrheitsgemäßen Wert zurückgibt (und undefined, wenn nichts gefunden wird).
> [6, -5, 8].find(x => x < 0)
-5
> [6, 5, 8].find(x => x < 0)
undefined.findIndex() gibt den Index des ersten Elements zurück, für das sein Callback einen wahrheitsgemäßen Wert zurückgibt (und -1, wenn nichts gefunden wird).
> [6, -5, 8].findIndex(x => x < 0)
1
> [6, 5, 8].findIndex(x => x < 0)
-1.findIndex() kann wie folgt implementiert werden:
function findIndex(arr, callback) {
for (const [i, x] of arr.entries()) {
if (callback(x, i, arr)) {
return i;
}
}
return -1;
}.map(): Kopieren und dabei Elemente neue Werte geben.map() gibt eine modifizierte Kopie des Empfängers zurück. Die Elemente der Kopie sind die Ergebnisse der Anwendung des Callbacks von map auf die Elemente des Empfängers.
All das ist leichter durch Beispiele zu verstehen:
> [1, 2, 3].map(x => x * 3)
[ 3, 6, 9 ]
> ['how', 'are', 'you'].map(str => str.toUpperCase())
[ 'HOW', 'ARE', 'YOU' ]
> [true, true, true].map((_x, index) => index)
[ 0, 1, 2 ].map() kann wie folgt implementiert werden:
function map(arr, mapFunc) {
const result = [];
for (const [i, x] of arr.entries()) {
result.push(mapFunc(x, i, arr));
}
return result;
} Übung: Zeilen nummerieren mit
.map()
exercises/arrays/number_lines_test.mjs
.flatMap(): zu null oder mehr Werten mappenDie Typsignatur von Array<T>.prototype.flatMap() ist:
.flatMap<U>(
callback: (value: T, index: number, array: T[]) => U|Array<U>,
thisValue?: any
): U[]Sowohl .map() als auch .flatMap() nehmen eine Funktion callback als Parameter, die steuert, wie ein Eingabe-Array in ein Ausgabe-Array übersetzt wird.
.map() wird jedes Eingabe-Array-Element in genau ein Ausgabe-Element übersetzt. Das heißt, callback gibt einen einzelnen Wert zurück..flatMap() wird jedes Eingabe-Array-Element in null oder mehr Ausgabe-Elemente übersetzt. Das heißt, callback gibt ein Array von Werten zurück (es kann auch Nicht-Array-Werte zurückgeben, aber das ist selten).Dies ist .flatMap() in Aktion:
> ['a', 'b', 'c'].flatMap(x => [x,x])
[ 'a', 'a', 'b', 'b', 'c', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [x])
[ 'a', 'b', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [])
[]Wir werden Anwendungsfälle als nächstes betrachten, bevor wir erforschen, wie diese Methode implementiert werden könnte.
Das Ergebnis der Array-Methode .map() hat immer die gleiche Länge wie das Array, auf dem es aufgerufen wird. Das heißt, sein Callback kann keine Array-Elemente überspringen, an denen es nicht interessiert ist. Die Fähigkeit von .flatMap(), dies zu tun, ist im nächsten Beispiel nützlich.
Wir verwenden die folgende Funktion processArray(), um ein Array zu erstellen, das wir dann per .flatMap() filtern und mappen werden:
function processArray(arr, callback) {
return arr.map(x => {
try {
return { value: callback(x) };
} catch (e) {
return { error: e };
}
});
}Als nächstes erstellen wir ein Array results über processArray():
const results = processArray([1, -5, 6], throwIfNegative);
assert.deepEqual(results, [
{ value: 1 },
{ error: new Error('Illegal value: -5') },
{ value: 6 },
]);
function throwIfNegative(value) {
if (value < 0) {
throw new Error('Illegal value: '+value);
}
return value;
}Wir können jetzt .flatMap() verwenden, um nur die Werte oder nur die Fehler aus results zu extrahieren:
const values = results.flatMap(
result => result.value ? [result.value] : []);
assert.deepEqual(values, [1, 6]);
const errors = results.flatMap(
result => result.error ? [result.error] : []);
assert.deepEqual(errors, [new Error('Illegal value: -5')]);Die Array-Methode .map() ordnet jedem Eingabe-Array-Element ein Ausgabe-Element zu. Aber was, wenn wir es auf mehrere Ausgabe-Elemente abbilden wollen?
Das wird im folgenden Beispiel notwendig:
> stringsToCodePoints(['many', 'a', 'moon'])
['m', 'a', 'n', 'y', 'a', 'm', 'o', 'o', 'n']Wir wollen ein Array von Strings in ein Array von Unicode-Zeichen (Codepunkten) umwandeln. Die folgende Funktion erreicht dies per .flatMap():
function stringsToCodePoints(strs) {
return strs.flatMap(str => Array.from(str));
}Wir können .flatMap() wie folgt implementieren. Hinweis: Diese Implementierung ist einfacher als die eingebaute Version, die z. B. mehr Prüfungen durchführt.
function flatMap(arr, mapFunc) {
const result = [];
for (const [index, elem] of arr.entries()) {
const x = mapFunc(elem, index, arr);
// We allow mapFunc() to return non-Arrays
if (Array.isArray(x)) {
result.push(...x);
} else {
result.push(x);
}
}
return result;
} Übungen:
.flatMap()
exercises/arrays/convert_to_numbers_test.mjs
exercises/arrays/replace_objects_test.mjs
.filter(): nur einige der Elemente behaltenDie Array-Methode .filter() gibt ein Array zurück, das alle Elemente sammelt, für die der Callback einen wahrheitsgemäßen Wert zurückgibt.
Zum Beispiel
> [-1, 2, 5, -7, 6].filter(x => x >= 0)
[ 2, 5, 6 ]
> ['a', 'b', 'c', 'd'].filter((_x,i) => (i%2)===0)
[ 'a', 'c' ].filter() kann wie folgt implementiert werden:
function filter(arr, filterFunc) {
const result = [];
for (const [i, x] of arr.entries()) {
if (filterFunc(x, i, arr)) {
result.push(x);
}
}
return result;
} Übung: Leere Zeilen mit
.filter() entfernen
exercises/arrays/remove_empty_lines_filter_test.mjs
.reduce(): einen Wert aus einem Array ableiten (Fortgeschrittene)Die Methode .reduce() ist ein mächtiges Werkzeug zur Berechnung einer „Zusammenfassung“ eines Arrays arr. Eine Zusammenfassung kann jede Art von Wert sein:
arr.arr, wobei jedes Element doppelt so groß wie das ursprüngliche Element ist.reduce ist in der funktionalen Programmierung auch als foldl („fold left“) bekannt und dort beliebt. Eine Einschränkung ist, dass es Code schwer verständlich machen kann.
.reduce() hat die folgende Typsignatur (innerhalb eines Array<T>):
.reduce<U>(
callback: (accumulator: U, element: T, index: number, array: T[]) => U,
init?: U)
: UT ist der Typ der Array-Elemente, U ist der Typ der Zusammenfassung. Die beiden können gleich oder verschieden sein. accumulator ist nur ein anderer Name für „Zusammenfassung“.
Um die Zusammenfassung eines Arrays arr zu berechnen, füttert .reduce() alle Array-Elemente nacheinander an seinen Callback:
const accumulator_0 = callback(init, arr[0]);
const accumulator_1 = callback(accumulator_0, arr[1]);
const accumulator_2 = callback(accumulator_1, arr[2]);
// Etc.callback kombiniert die zuvor berechnete Zusammenfassung (gespeichert in seinem Parameter accumulator) mit dem aktuellen Array-Element und gibt den nächsten accumulator zurück. Das Ergebnis von .reduce() ist der finale Accumulator – das letzte Ergebnis von callback, nachdem es alle Elemente besucht hat.
Anders ausgedrückt: callback leistet die meiste Arbeit; .reduce() ruft es nur auf nützliche Weise auf.
Man könnte sagen, dass der Callback Array-Elemente in den Accumulator "gefaltet" (folded) werden. Deshalb wird diese Operation in der funktionalen Programmierung "fold" genannt.
Betrachten wir ein Beispiel für .reduce() in Aktion: die Funktion addAll() berechnet die Summe aller Zahlen in einem Array arr.
function addAll(arr) {
const startSum = 0;
const callback = (sum, element) => sum + element;
return arr.reduce(callback, startSum);
}
assert.equal(addAll([1, 2, 3]), 6); // (A)
assert.equal(addAll([7, -4, 2]), 5);In diesem Fall enthält der Accumulator die Summe aller Array-Elemente, die callback bereits besucht hat.
Wie wurde das Ergebnis 6 aus dem Array in Zeile A abgeleitet? Über die folgenden Aufrufe von callback:
callback(0, 1) --> 1
callback(1, 2) --> 3
callback(3, 3) --> 6
Hinweise
init von .reduce()).callback ist auch das Ergebnis von .reduce().Alternativ hätten wir addAll() über eine for-of-Schleife implementieren können:
function addAll(arr) {
let sum = 0;
for (const element of arr) {
sum = sum + element;
}
return sum;
}Es ist schwer zu sagen, welche der beiden Implementierungen "besser" ist: die auf .reduce() basierende ist etwas prägnanter, während die auf for-of basierende möglicherweise etwas leichter zu verstehen ist – besonders wenn jemand mit funktionaler Programmierung nicht vertraut ist.
.reduce() findenDie folgende Funktion ist eine Implementierung der Array-Methode .indexOf(). Sie gibt den ersten Index zurück, an dem der gegebene searchValue im Array arr vorkommt:
const NOT_FOUND = -1;
function indexOf(arr, searchValue) {
return arr.reduce(
(result, elem, index) => {
if (result !== NOT_FOUND) {
// We have already found something: don’t change anything
return result;
} else if (elem === searchValue) {
return index;
} else {
return NOT_FOUND;
}
},
NOT_FOUND);
}
assert.equal(indexOf(['a', 'b', 'c'], 'b'), 1);
assert.equal(indexOf(['a', 'b', 'c'], 'x'), -1);Eine Einschränkung von .reduce() ist, dass wir nicht vorzeitig abbrechen können (in einer for-of-Schleife können wir break verwenden). Hier geben wir das Ergebnis sofort zurück, sobald wir es gefunden haben.
Die Funktion double(arr) gibt eine Kopie von inArr zurück, deren Elemente alle mit 2 multipliziert sind
function double(inArr) {
return inArr.reduce(
(outArr, element) => {
outArr.push(element * 2);
return outArr;
},
[]);
}
assert.deepEqual(
double([1, 2, 3]),
[2, 4, 6]);Wir modifizieren den Anfangswert [], indem wir hinein pushen. Eine nicht-destruktive, funktionalere Version von double() sieht wie folgt aus
function double(inArr) {
return inArr.reduce(
// Don’t change `outArr`, return a fresh Array
(outArr, element) => [...outArr, element * 2],
[]);
}
assert.deepEqual(
double([1, 2, 3]),
[2, 4, 6]);Diese Version ist eleganter, aber auch langsamer und verbraucht mehr Speicher.
Aufgaben:
.reduce()
map() via .reduce(): exercises/arrays/map_via_reduce_test.mjsfilter() via .reduce(): exercises/arrays/filter_via_reduce_test.mjscountMatches() via .reduce(): exercises/arrays/count_matches_via_reduce_test.mjs.sort(): Arrays sortieren.sort() hat die folgende Typdefinition
sort(compareFunc?: (a: T, b: T) => number): thisStandardmäßig sortiert .sort() Zeichenkettendarstellungen der Elemente. Diese Darstellungen werden über < verglichen. Dieser Operator vergleicht lexikografisch (die ersten Zeichen sind am bedeutsamsten). Dies sehen wir beim Sortieren von Zahlen
> [200, 3, 10].sort()
[ 10, 200, 3 ]Beim Sortieren von Zeichenketten in natürlicher Sprache müssen wir bedenken, dass sie gemäß ihren Code-Einheiten-Werten (Zeichencodes) verglichen werden
> ['pie', 'cookie', 'éclair', 'Pie', 'Cookie', 'Éclair'].sort()
[ 'Cookie', 'Pie', 'cookie', 'pie', 'Éclair', 'éclair' ]Alle unakzentuierten Großbuchstaben kommen vor allen unakzentuierten Kleinbuchstaben, die wiederum vor allen akzentuierten Buchstaben kommen. Wir können Intl, die JavaScript Internationalization API verwenden, wenn wir eine korrekte Sortierung für natürliche Sprachen wünschen.
.sort() sortiert in place; es ändert und gibt seinen Empfänger zurück
> const arr = ['a', 'c', 'b'];
> arr.sort() === arr
true
> arr
[ 'a', 'b', 'c' ]Wir können die Sortierreihenfolge über den Parameter compareFunc anpassen, der eine Zahl zurückgeben muss, die
a < ba === ba > b Tipp zum Merken dieser Regeln
Eine negative Zahl ist kleiner als null (usw.).
Wir können diese Hilfsfunktion verwenden, um Zahlen zu sortieren
function compareNumbers(a, b) {
if (a < b) {
return -1;
} else if (a === b) {
return 0;
} else {
return 1;
}
}
assert.deepEqual(
[200, 3, 10].sort(compareNumbers),
[3, 10, 200]);Das Folgende ist eine schnelle und schmutzige Alternative.
> [200, 3, 10].sort((a,b) => a - b)
[ 3, 10, 200 ]Die Nachteile dieses Ansatzes sind
a-b zu einer großen positiven oder negativen Zahl wird.Wir müssen auch eine Vergleichsfunktion verwenden, wenn wir Objekte sortieren wollen. Als Beispiel zeigt der folgende Code, wie Objekte nach Alter sortiert werden.
const arr = [ {age: 200}, {age: 3}, {age: 10} ];
assert.deepEqual(
arr.sort((obj1, obj2) => obj1.age - obj2.age),
[{ age: 3 }, { age: 10 }, { age: 200 }] ); Aufgabe: Objekte nach Namen sortieren
exercises/arrays/sort_objects_test.mjs
ArrayLegende
R: Die Methode ändert das Array nicht (nicht-destruktiv).W: Die Methode ändert das Array (destruktiv).new Array()new Array(n) erstellt ein Array der Länge n, das n Lücken enthält
// Trailing commas are always ignored.
// Therefore: number of commas = number of holes
assert.deepEqual(new Array(3), [,,,]);new Array() erstellt ein leeres Array. Ich empfehle jedoch, stattdessen immer [] zu verwenden.
ArrayArray.from<T>(iterable: Iterable<T> | ArrayLike<T>): T[] [ES6]
Array.from<T,U>(iterable: Iterable<T> | ArrayLike<T>, mapFunc: (v: T, k: number) => U, thisArg?: any): U[] [ES6]
Konvertiert ein Iterable oder ein Array-ähnliches Objekt in ein Array. Optional können die Eingabewerte über mapFunc transformiert werden, bevor sie dem Ausgabe-Array hinzugefügt werden.
Beispiele
> Array.from(new Set(['a', 'b'])) // iterable
[ 'a', 'b' ]
> Array.from({length: 2, 0:'a', 1:'b'}) // Array-like object
[ 'a', 'b' ]Array.of<T>(...items: T[]): T[] [ES6]
Diese statische Methode ist hauptsächlich für Unterklassen von Array nützlich, wo sie als benutzerdefinierter Array-Literal dient
class MyArray extends Array {}
assert.equal(
MyArray.of('a', 'b') instanceof MyArray, true);Array.prototype.at(index: number): T | undefined [R, ES2022]
Gibt das Array-Element am index zurück. Wenn index negativ ist, wird er vor der Verwendung zu .length addiert (-1 wird zu this.length-1 usw.).
> ['a', 'b', 'c'].at(0)
'a'
> ['a', 'b', 'c'].at(-1)
'c'.concat(...items: Array<T[] | T>): T[] [R, ES3]
Gibt ein neues Array zurück, das die Konkatenation des Empfängers und aller items ist. Nicht-Array-Parameter (wie 'b' im folgenden Beispiel) werden so behandelt, als wären sie Arrays mit einzelnen Elementen.
> ['a'].concat('b', ['c', 'd'])
[ 'a', 'b', 'c', 'd' ].copyWithin(target: number, start: number, end=this.length): this [W, ES6]
Kopiert die Elemente, deren Indizes von (einschließlich) start bis (ausschließlich) end reichen, an Indizes, die mit target beginnen. Überlappungen werden korrekt behandelt.
> ['a', 'b', 'c', 'd'].copyWithin(0, 2, 4)
[ 'c', 'd', 'c', 'd' ]Wenn start oder end negativ ist, wird .length dazu addiert.
.entries(): Iterable<[number, T]> [R, ES6]
Gibt ein Iterable von [index, element]-Paaren zurück.
> Array.from(['a', 'b'].entries())
[ [ 0, 'a' ], [ 1, 'b' ] ].every(callback: (value: T, index: number, array: Array<T>) => boolean, thisArg?: any): boolean [R, ES5]
Gibt true zurück, wenn callback für jedes Element einen wahrheitsgemäßen Wert zurückgibt. Andernfalls gibt es false zurück. Es stoppt, sobald es einen falsifizierbaren Wert erhält. Diese Methode entspricht der universellen Quantifizierung ("für alle", ∀) in der Mathematik.
> [1, 2, 3].every(x => x > 0)
true
> [1, -2, 3].every(x => x > 0)
falseVerwandte Methode: .some() ("existiert").
.fill(value: T, start=0, end=this.length): this [W, ES6]
Weist value jedem Index zwischen (einschließlich) start und (ausschließlich) end zu.
> [0, 1, 2].fill('a')
[ 'a', 'a', 'a' ]Vorsicht: Verwenden Sie diese Methode nicht, um ein Array mit einem Objekt obj zu füllen; dann wird jedes Element auf obj verweisen (es wird geteilt). In diesem Fall ist es besser, Array.from() zu verwenden.
.filter(callback: (value: T, index: number, array: Array<T>) => any, thisArg?: any): T[] [R, ES5]
Gibt ein Array mit nur den Elementen zurück, für die callback einen wahrheitsgemäßen Wert zurückgibt.
> [1, -2, 3].filter(x => x > 0)
[ 1, 3 ].find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined [R, ES6]
Das Ergebnis ist das erste Element, für das predicate einen wahrheitsgemäßen Wert zurückgibt. Wenn kein solches Element vorhanden ist, ist das Ergebnis undefined.
> [1, -2, 3].find(x => x < 0)
-2
> [1, 2, 3].find(x => x < 0)
undefined.findIndex(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): number [R, ES6]
Das Ergebnis ist der Index des ersten Elements, für das predicate einen wahrheitsgemäßen Wert zurückgibt. Wenn kein solches Element vorhanden ist, ist das Ergebnis -1.
> [1, -2, 3].findIndex(x => x < 0)
1
> [1, 2, 3].findIndex(x => x < 0)
-1.flat(depth = 1): any[] [R, ES2019]
"Glättet" ein Array: Es steigt in die Arrays ein, die im Eingabe-Array verschachtelt sind, und erstellt eine Kopie, bei der alle Werte, die es auf Ebene depth oder tiefer findet, in die oberste Ebene verschoben werden.
> [ 1,2, [3,4], [[5,6]] ].flat(0) // no change
[ 1, 2, [3,4], [[5,6]] ]
> [ 1,2, [3,4], [[5,6]] ].flat(1)
[1, 2, 3, 4, [5,6]]
> [ 1,2, [3,4], [[5,6]] ].flat(2)
[1, 2, 3, 4, 5, 6].flatMap<U>(callback: (value: T, index: number, array: T[]) => U|Array<U>, thisValue?: any): U[] [R, ES2019]
Das Ergebnis wird erzeugt, indem callback() für jedes Element des ursprünglichen Arrays aufgerufen wird und die von ihm zurückgegebenen Arrays verkettet werden.
> ['a', 'b', 'c'].flatMap(x => [x,x])
[ 'a', 'a', 'b', 'b', 'c', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [x])
[ 'a', 'b', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [])
[].forEach(callback: (value: T, index: number, array: Array<T>) => void, thisArg?: any): void [R, ES5]
Ruft callback für jedes Element auf.
['a', 'b'].forEach((x, i) => console.log(x, i))
// Output:
// 'a', 0
// 'b', 1Eine for-of-Schleife ist normalerweise die bessere Wahl: Sie ist schneller, unterstützt break und kann über beliebige Iterables iterieren.
.includes(searchElement: T, fromIndex=0): boolean [R, ES2016]
Gibt true zurück, wenn der Empfänger ein Element hat, dessen Wert searchElement ist, und andernfalls false. Die Suche beginnt am Index fromIndex.
> [0, 1, 2].includes(1)
true
> [0, 1, 2].includes(5)
false.indexOf(searchElement: T, fromIndex=0): number [R, ES5]
Gibt den Index des ersten Elements zurück, das strikt gleich searchElement ist. Gibt -1 zurück, wenn kein solches Element vorhanden ist. Beginnt die Suche am Index fromIndex und besucht danach höhere Indizes.
> ['a', 'b', 'a'].indexOf('a')
0
> ['a', 'b', 'a'].indexOf('a', 1)
2
> ['a', 'b', 'a'].indexOf('c')
-1.join(separator = ','): string [R, ES1]
Erstellt eine Zeichenkette durch Konkatenation von Zeichenkettendarstellungen aller Elemente, getrennt durch separator.
> ['a', 'b', 'c'].join('##')
'a##b##c'
> ['a', 'b', 'c'].join()
'a,b,c'.keys(): Iterable<number> [R, ES6]
Gibt ein Iterable über die Schlüssel des Empfängers zurück.
> Array.from(['a', 'b'].keys())
[ 0, 1 ].lastIndexOf(searchElement: T, fromIndex=this.length-1): number [R, ES5]
Gibt den Index des letzten Elements zurück, das strikt gleich searchElement ist. Gibt -1 zurück, wenn kein solches Element vorhanden ist. Beginnt die Suche am Index fromIndex und besucht danach niedrigere Indizes.
> ['a', 'b', 'a'].lastIndexOf('a')
2
> ['a', 'b', 'a'].lastIndexOf('a', 1)
0
> ['a', 'b', 'a'].lastIndexOf('c')
-1.map<U>(mapFunc: (value: T, index: number, array: Array<T>) => U, thisArg?: any): U[] [R, ES5]
Gibt ein neues Array zurück, in dem jedes Element das Ergebnis von mapFunc ist, angewendet auf das entsprechende Element des Empfängers.
> [1, 2, 3].map(x => x * 2)
[ 2, 4, 6 ]
> ['a', 'b', 'c'].map((x, i) => i)
[ 0, 1, 2 ].pop(): T | undefined [W, ES3]
Entfernt und gibt das letzte Element des Empfängers zurück. Das heißt, es behandelt das Ende des Empfängers als Stack. Das Gegenteil von .push().
> const arr = ['a', 'b', 'c'];
> arr.pop()
'c'
> arr
[ 'a', 'b' ].push(...items: T[]): number [W, ES3]
Fügt null oder mehr items am Ende des Empfängers hinzu. Das heißt, es behandelt das Ende des Empfängers als Stack. Der Rückgabewert ist die Länge des Empfängers nach der Änderung. Das Gegenteil von .pop().
> const arr = ['a', 'b'];
> arr.push('c', 'd')
4
> arr
[ 'a', 'b', 'c', 'd' ]Wir können ein Array pushen, indem wir es (...) in Argumente sprengen
> const arr = ['x'];
> arr.push(...['y', 'z'])
3
> arr
[ 'x', 'y', 'z' ] .reduce<U>(callback: (accumulator: U, element: T, index: number, array: T[]) => U, init?: U): U [R, ES5]
Diese Methode erzeugt eine Zusammenfassung des Empfängers: Sie füttert alle Array-Elemente an callback, das eine aktuelle Zusammenfassung (im Parameter accumulator) mit dem aktuellen Array-Element kombiniert und den nächsten accumulator zurückgibt
const accumulator_0 = callback(init, arr[0]);
const accumulator_1 = callback(accumulator_0, arr[1]);
const accumulator_2 = callback(accumulator_1, arr[2]);
// Etc.Das Ergebnis von .reduce() ist das letzte Ergebnis von callback, nachdem es alle Array-Elemente besucht hat.
> [1, 2, 3].reduce((accu, x) => accu + x, 0)
6
> [1, 2, 3].reduce((accu, x) => accu + String(x), '')
'123'Wenn kein init bereitgestellt wird, wird das Array-Element an Index 0 verwendet und das Element an Index 1 wird zuerst besucht. Daher muss das Array mindestens die Länge 1 haben.
.reduceRight<U>(callback: (accumulator: U, element: T, index: number, array: T[]) => U, init?: U): U [R, ES5]
Funktioniert wie .reduce(), besucht die Array-Elemente aber rückwärts, beginnend mit dem letzten Element.
> [1, 2, 3].reduceRight((accu, x) => accu + String(x), '')
'321'.reverse(): this [W, ES1]
Ordnet die Elemente des Empfängers so neu an, dass sie in umgekehrter Reihenfolge stehen, und gibt dann den Empfänger zurück.
> const arr = ['a', 'b', 'c'];
> arr.reverse()
[ 'c', 'b', 'a' ]
> arr
[ 'c', 'b', 'a' ].shift(): T | undefined [W, ES3]
Entfernt und gibt das erste Element des Empfängers zurück. Das Gegenteil von .unshift().
> const arr = ['a', 'b', 'c'];
> arr.shift()
'a'
> arr
[ 'b', 'c' ].slice(start=0, end=this.length): T[] [R, ES3]
Gibt ein neues Array zurück, das die Elemente des Empfängers enthält, deren Indizes zwischen (einschließlich) start und (ausschließlich) end liegen.
> ['a', 'b', 'c', 'd'].slice(1, 3)
[ 'b', 'c' ]
> ['a', 'b'].slice() // shallow copy
[ 'a', 'b' ]Negative Indizes sind erlaubt und werden zu .length addiert
> ['a', 'b', 'c'].slice(-2)
[ 'b', 'c' ].some(callback: (value: T, index: number, array: Array<T>) => boolean, thisArg?: any): boolean [R, ES5]
Gibt true zurück, wenn callback für mindestens ein Element einen wahrheitsgemäßen Wert zurückgibt. Andernfalls gibt es false zurück. Es stoppt, sobald es einen wahrheitsgemäßen Wert erhält. Diese Methode entspricht der existenziellen Quantifizierung ("existiert", ∃) in der Mathematik.
> [1, 2, 3].some(x => x < 0)
false
> [1, -2, 3].some(x => x < 0)
trueVerwandte Methode: .every() ("für alle").
.sort(compareFunc?: (a: T, b: T) => number): this [W, ES1]
Sortiert den Empfänger und gibt ihn zurück. Standardmäßig sortiert er Zeichenkettendarstellungen der Elemente. Dies geschieht lexikografisch und nach den Code-Einheiten-Werten (Zeichencodes) der Zeichen
> ['pie', 'cookie', 'éclair', 'Pie', 'Cookie', 'Éclair'].sort()
[ 'Cookie', 'Pie', 'cookie', 'pie', 'Éclair', 'éclair' ]
> [200, 3, 10].sort()
[ 10, 200, 3 ]Wir können die Sortierreihenfolge über compareFunc anpassen, das eine Zahl zurückgibt, die
a < ba === ba > bTrick zum Sortieren von Zahlen (mit dem Risiko von numerischem Über- oder Unterlauf)
> [200, 3, 10].sort((a, b) => a - b)
[ 3, 10, 200 ]
.sort() ist stabil
Seit ECMAScript 2019 ist garantiert, dass die Sortierung stabil ist: Wenn Elemente beim Sortieren als gleich betrachtet werden, ändert die Sortierung die Reihenfolge dieser Elemente (relativ zueinander) nicht.
.splice(start: number, deleteCount=this.length-start, ...items: T[]): T[] [W, ES3]
Am Index start werden deleteCount Elemente entfernt und die items eingefügt. Die entfernten Elemente werden zurückgegeben.
> const arr = ['a', 'b', 'c', 'd'];
> arr.splice(1, 2, 'x', 'y')
[ 'b', 'c' ]
> arr
[ 'a', 'x', 'y', 'd' ]start kann negativ sein und wird zu .length addiert, wenn es das ist
> ['a', 'b', 'c'].splice(-2, 2)
[ 'b', 'c' ].toString(): string [R, ES1]
Konvertiert alle Elemente mithilfe von String() in Zeichenketten, verkettet sie, trennt sie mit Kommas und gibt das Ergebnis zurück.
> [1, 2, 3].toString()
'1,2,3'
> ['1', '2', '3'].toString()
'1,2,3'
> [].toString()
''.unshift(...items: T[]): number [W, ES3]
Fügt die items am Anfang des Empfängers ein und gibt dessen Länge nach dieser Änderung zurück.
> const arr = ['c', 'd'];
> arr.unshift('e', 'f')
4
> arr
[ 'e', 'f', 'c', 'd' ].values(): Iterable<T> [R, ES6]
Gibt ein Iterable über die Werte des Empfängers zurück.
> Array.from(['a', 'b'].values())
[ 'a', 'b' ] Quiz
Siehe Quiz-App.