Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.Erfahre mehr über dieses Experiment.
Array.prototype.reduce()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since Juli 2015.
Diereduce()
Methode vonArray
Instanzen führt eine vom Benutzer bereitgestellte "Reducer"-Callback-Funktion für jedes Element des Arrays in Reihenfolge aus und übergibt den Rückgabewert der vorhergehenden Berechnung. Das Endergebnis des Ausführens des Reducers über alle Elemente des Arrays ist ein einzelner Wert.
Bei der ersten Ausführung des Callbacks gibt es keinen "Rückgabewert der vorhergehenden Berechnung". Falls angegeben, kann ein Anfangswert stattdessen verwendet werden. Andernfalls wird das Array-Element an Index 0 als Anfangswert verwendet und die Iteration beginnt beim nächsten Element (Index 1 statt Index 0).
In diesem Artikel
Probieren Sie es aus
const array = [1, 2, 3, 4];// 0 + 1 + 2 + 3 + 4const initialValue = 0;const sumWithInitial = array.reduce( (accumulator, currentValue) => accumulator + currentValue, initialValue,);console.log(sumWithInitial);// Expected output: 10
Syntax
reduce(callbackFn)reduce(callbackFn, initialValue)
Parameter
callbackFn
Eine Funktion, die für jedes Element im Array ausgeführt wird. Ihr Rückgabewert wird zum Wert des
accumulator
-Parameters beim nächsten Aufruf voncallbackFn
. Beim letzten Aufruf wird der Rückgabewert zum Rückgabewert vonreduce()
. Die Funktion wird mit den folgenden Argumenten aufgerufen:accumulator
Der Wert, der aus dem vorhergehenden Aufruf von
callbackFn
resultiert. Beim ersten Aufruf ist sein WertinitialValue
, falls letzterer angegeben ist; andernfalls ist sein Wertarray[0]
.currentValue
Der Wert des aktuellen Elements. Beim ersten Aufruf ist sein Wert
array[0]
, wenninitialValue
angegeben ist; andernfalls ist sein Wertarray[1]
.currentIndex
Die Indexposition von
currentValue
im Array. Beim ersten Aufruf ist sein Wert0
, wenninitialValue
angegeben ist, andernfalls1
.array
Das Array, auf dem
reduce()
aufgerufen wurde.
initialValue
OptionalEin Wert, mit dem
accumulator
beim ersten Aufruf des Callbacks initialisiert wird.FallsinitialValue
angegeben ist, beginntcallbackFn
mit dem ersten Wert im Array alscurrentValue
.FallsinitialValue
nicht angegeben ist, wirdaccumulator
mit dem ersten Wert im Array initialisiert, undcallbackFn
beginnt mit dem zweiten Wert im Array alscurrentValue
. In diesem Fall, wenn das Array leer ist (sodass es keinen ersten Wert gibt, der alsaccumulator
zurückgegeben werden könnte), wird ein Fehler ausgelöst.
Rückgabewert
Der Wert, der aus dem vollständigen Ausführen der "Reducer"-Callback-Funktion über das gesamte Array resultiert.
Ausnahmen
TypeError
Wird ausgelöst, wenn das Array keine Elemente enthält und
initialValue
nicht angegeben ist.
Beschreibung
Diereduce()
Methode ist eineiterative Methode. Sie führt eine "Reducer"-Callback-Funktion über alle Elemente des Arrays in aufsteigender Indexreihenfolge aus und akkumuliert sie zu einem einzigen Wert. Jedes Mal wird der Rückgabewert voncallbackFn
beim nächsten Aufruf erneut incallbackFn
alsaccumulator
übergeben. Der endgültige Wert vonaccumulator
(der der voncallbackFn
bei der letzten Iteration über das Array zurückgegebene Wert ist) wird der Rückgabewert vonreduce()
. Lesen Sie den Abschnitt überiterative Methoden für weitere Informationen darüber, wie diese Methoden allgemein funktionieren.
callbackFn
wird nur für Array-Indizes aufgerufen, denen Werte zugewiesen wurden. Es wird nicht für leere Stellen indünn besiedelten Arrays aufgerufen.
Im Gegensatz zu andereniterativen Methoden akzeptiertreduce()
keinthisArg
Argument.callbackFn
wird immer mitundefined
alsthis
aufgerufen, das durchglobalThis
ersetzt wird, fallscallbackFn
nicht strikt ist.
reduce()
ist ein zentrales Konzept in derfunktionalen Programmierung, wo es nicht möglich ist, einen Wert zu verändern. Um die Werte in einem Array zu akkumulieren, muss man daher bei jeder Iteration einen neuen Akkumulator-Wert zurückgeben. Diese Konvention setzt sich in JavaScriptsreduce()
fort: Sie solltenSpread-Syntax oder andere Kopiermethoden verwenden, um neue Arrays und Objekte als Akkumulator zu erstellen, anstatt das bestehende zu verändern. Wenn Sie sich entscheiden, den Akkumulator anstatt zu kopieren zu manipulieren, erinnern Sie sich daran, dennoch das modifizierte Objekt im Callback zurückzugeben, oder die nächste Iteration erhältundefined
. Beachten Sie jedoch, dass das Kopieren des Akkumulators zu erhöhtem Speicherverbrauch und verschlechterter Leistung führen kann — sehen SieWann man reduce() nicht verwenden sollte für mehr Details. In solchen Fällen ist es besser, einefor
-Schleife zu verwenden, um schlechte Leistung und unverständlichen Code zu vermeiden.
Diereduce()
Methode istgenerisch. Sie erwartet nur, dass derthis
-Wert einelength
-Eigenschaft und integer-indizierte Eigenschaften hat.
Randfälle
Hat das Array nur ein Element (unabhängig von der Position) und es wird keininitialValue
bereitgestellt, oder wenninitialValue
bereitgestellt wird, das Array aber leer ist, wird der Einzelwertohne Aufruf voncallbackFn
zurückgegeben.
WirdinitialValue
bereitgestellt und ist das Array nicht leer, wird die Reduce-Methode die Callback-Funktion immer ab Index 0 aufrufen.
WirdinitialValue
nicht bereitgestellt, verhält sich die Reduce-Methode unterschiedlich für Arrays mit einer Länge größer als 1, gleich 1 und 0, wie im folgenden Beispiel gezeigt:
const getMax = (a, b) => Math.max(a, b);// callback is invoked for each element in the array starting at index 0[1, 100].reduce(getMax, 50); // 100[50].reduce(getMax, 10); // 50// callback is invoked once for element at index 1[1, 100].reduce(getMax); // 100// callback is not invoked[50].reduce(getMax); // 50[].reduce(getMax, 1); // 1[].reduce(getMax); // TypeError
Beispiele
>Wie reduce() ohne Anfangswert funktioniert
Der folgende Code zeigt, was passiert, wenn wirreduce()
mit einem Array und ohne Anfangswert aufrufen.
const array = [15, 16, 17, 18, 19];function reducer(accumulator, currentValue, index) { const returns = accumulator + currentValue; console.log( `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`, ); return returns;}array.reduce(reducer);
Das Callback würde viermal aufgerufen werden, mit den Argumenten und Rückgabewerten bei jedem Aufruf folgendermaßen:
accumulator | currentValue | index | Rückgabewert | |
---|---|---|---|---|
Erster Aufruf | 15 | 16 | 1 | 31 |
Zweiter Aufruf | 31 | 17 | 2 | 48 |
Dritter Aufruf | 48 | 18 | 3 | 66 |
Vierter Aufruf | 66 | 19 | 4 | 85 |
Derarray
Parameter ändert sich während des Prozesses nie – er ist immer[15, 16, 17, 18, 19]
. Der vonreduce()
zurückgegebene Wert wäre der letzte Rückgabewert des letzten Callback-Aufrufs (85
).
Wie reduce() mit einem Anfangswert funktioniert
Hier reduzieren wir dasselbe Array mit demselben Algorithmus, aber mit eineminitialValue
von10
, der als zweites Argument anreduce()
übergeben wird:
[15, 16, 17, 18, 19].reduce( (accumulator, currentValue) => accumulator + currentValue, 10,);
Das Callback würde fünfmal aufgerufen werden, mit den Argumenten und Rückgabewerten bei jedem Aufruf folgendermaßen:
accumulator | currentValue | index | Rückgabewert | |
---|---|---|---|---|
Erster Aufruf | 10 | 15 | 0 | 25 |
Zweiter Aufruf | 25 | 16 | 1 | 41 |
Dritter Aufruf | 41 | 17 | 2 | 58 |
Vierter Aufruf | 58 | 18 | 3 | 76 |
Fünfter Aufruf | 76 | 19 | 4 | 95 |
Der vonreduce()
zurückgegebene Wert in diesem Fall wäre95
.
Summe von Werten in einem Objekt-Array
Um die in einem Array von Objekten enthaltenen Werte zu summieren,müssen Sie eineninitialValue
angeben, damit jedes Element Ihre Funktion durchläuft.
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];const sum = objects.reduce( (accumulator, currentValue) => accumulator + currentValue.x, 0,);console.log(sum); // 6
Funktional sequentielles Piping
Diepipe
-Funktion nimmt eine Abfolge von Funktionen und gibt eine neue Funktion zurück. Wenn die neue Funktion mit einem Argument aufgerufen wird, werden die Funktionen in Reihenfolge aufgerufen, wobei jede den Rückgabewert der vorherigen Funktion erhält.
const pipe = (...functions) => (initialValue) => functions.reduce((acc, fn) => fn(acc), initialValue);// Building blocks to use for compositionconst double = (x) => 2 * x;const triple = (x) => 3 * x;const quadruple = (x) => 4 * x;// Composed functions for multiplication of specific valuesconst multiply6 = pipe(double, triple);const multiply9 = pipe(triple, triple);const multiply16 = pipe(quadruple, quadruple);const multiply24 = pipe(double, triple, quadruple);// Usagemultiply6(6); // 36multiply9(9); // 81multiply16(16); // 256multiply24(10); // 240
Ausführen von Promises in der Reihenfolge
Promise-Sequenzierung ist im Wesentlichen das in dem vorherigen Abschnitt demonstrierte Funktional-Piping, das jedoch asynchron ausgeführt wird.
// Compare this with pipe: fn(acc) is changed to acc.then(fn),// and initialValue is ensured to be a promiseconst asyncPipe = (...functions) => (initialValue) => functions.reduce((acc, fn) => acc.then(fn), Promise.resolve(initialValue));// Building blocks to use for compositionconst p1 = async (a) => a * 5;const p2 = async (a) => a * 2;// The composed functions can also return non-promises, because the values are// all eventually wrapped in promisesconst f3 = (a) => a * 3;const p4 = async (a) => a * 4;asyncPipe(p1, p2, f3, p4)(10).then(console.log); // 1200
asyncPipe
kann auch mitasync
/await
implementiert werden, was seine Ähnlichkeit mitpipe
besser demonstriert:
const asyncPipe = (...functions) => (initialValue) => functions.reduce(async (acc, fn) => fn(await acc), initialValue);
Verwendung von reduce() mit dünn besiedelten Arrays
reduce()
überspringt fehlende Elemente in dünn besiedelten Arrays, aber es überspringt keineundefined
Werte.
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN
Aufrufen von reduce() bei Nicht-Array-Objekten
Diereduce()
Methode liest dielength
-Eigenschaft vonthis
und greift dann auf jede Eigenschaft zu, deren Schlüssel eine nicht-negative Ganzzahl kleiner alslength
ist.
const arrayLike = { length: 3, 0: 2, 1: 3, 2: 4, 3: 99, // ignored by reduce() since length is 3};console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));// 9
Wann man reduce() nicht verwenden sollte
Allzweck-Higher-Order-Funktionen wiereduce()
können mächtig, aber manchmal schwer zu verstehen sein, besonders für weniger erfahrene JavaScript-Entwickler. Wenn der Code bei der Verwendung anderer Array-Methoden klarer wird, müssen Entwickler den Lesbarkeitskompromiss gegen die anderen Vorteile der Verwendung vonreduce()
abwägen.
Beachten Sie, dassreduce()
immer gleichwertig zu einerfor...of
-Schleife ist, außer dass anstatt einer Variable im oberen Gültigkeitsbereich zu ändern, nun bei jeder Iteration der neue Wert zurückgegeben wird:
const val = array.reduce((acc, cur) => update(acc, cur), initialValue);// Is equivalent to:let val = initialValue;for (const cur of array) { val = update(val, cur);}
Wie bereits erwähnt, kann es sein, dass Leutereduce()
verwenden möchten, um funktionale Programmierungspraktiken von unveränderlichen Daten nachzuahmen. Entwickler, die die Unveränderlichkeit des Akkumulators aufrechterhalten, kopieren daher oft den gesamten Akkumulator bei jeder Iteration, wie folgt:
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = names.reduce((allNames, name) => { const currCount = Object.hasOwn(allNames, name) ? allNames[name] : 0; return { ...allNames, [name]: currCount + 1, };}, {});
Dieser Code ist leistungsschwach, da jede Iteration das gesamteallNames
-Objekt kopieren muss, das abhängig von der Anzahl der eindeutigen Namen groß sein könnte. Dieser Code hat eine Worst-Case-Performance vonO(N^2)
, wobeiN
die Länge vonnames
ist.
Eine bessere Alternative ist es, dasallNames
-Objekt bei jeder Iteration zuverändern. WennallNames
jedoch sowieso verändert wird, sollten Siereduce()
in einefor
-Schleife umwandeln, was viel klarer ist:
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = names.reduce((allNames, name) => { const currCount = allNames[name] ?? 0; allNames[name] = currCount + 1; // return allNames, otherwise the next iteration receives undefined return allNames;}, Object.create(null));
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = Object.create(null);for (const name of names) { const currCount = countedNames[name] ?? 0; countedNames[name] = currCount + 1;}
Wenn Ihr Akkumulator also ein Array oder Objekt ist und Sie das Array oder Objekt bei jeder Iteration kopieren, können Sie versehentlich quadratische Komplexität in Ihren Code einführen, was zu einer schnellen Verschlechterung der Leistung bei großen Datenmengen führt. Dies ist im realen Code passiert – siehe zum BeispielMaking Tanstack Table 1000x faster with a 1 line change.
Einige der akzeptablen Anwendungsfälle vonreduce()
sind oben gegeben (insbesondere, das Summieren eines Arrays, Promise-Sequenzierung und Funktion-Piping). Es gibt andere Fälle, in denen es bessere Alternativen zureduce()
gibt.
Flachlegen eines Arrays von Arrays. Verwenden Sie
flat()
stattdessen.jsconst flattened = array.reduce((acc, cur) => acc.concat(cur), []);
jsconst flattened = array.flat();
Gruppieren von Objekten nach einer Eigenschaft. Verwenden Sie
Object.groupBy()
stattdessen.jsconst groups = array.reduce((acc, obj) => { const key = obj.name; const curGroup = acc[key] ?? []; return { ...acc, [key]: [...curGroup, obj] };}, {});
jsconst groups = Object.groupBy(array, (obj) => obj.name);
Zusammenfügen von Arrays, die in einem Array von Objekten enthalten sind. Verwenden Sie
flatMap()
stattdessen.jsconst friends = [ { name: "Anna", books: ["Bible", "Harry Potter"] }, { name: "Bob", books: ["War and peace", "Romeo and Juliet"] }, { name: "Alice", books: ["The Lord of the Rings", "The Shining"] },];const allBooks = friends.reduce((acc, cur) => [...acc, ...cur.books], []);
jsconst allBooks = friends.flatMap((person) => person.books);
Entfernen von Duplikaten in einem Array. Verwenden Sie
Set
undArray.from()
stattdessen.jsconst uniqArray = array.reduce( (acc, cur) => (acc.includes(cur) ? acc : [...acc, cur]), [],);
jsconst uniqArray = Array.from(new Set(array));
Eliminieren oder Hinzufügen von Elementen in einem Array. Verwenden Sie
flatMap()
stattdessen.js// Takes an array of numbers and splits perfect squares into its square rootsconst roots = array.reduce((acc, cur) => { if (cur < 0) return acc; const root = Math.sqrt(cur); if (Number.isInteger(root)) return [...acc, root, root]; return [...acc, cur];}, []);
jsconst roots = array.flatMap((val) => { if (val < 0) return []; const root = Math.sqrt(val); if (Number.isInteger(root)) return [root, root]; return [val];});
Wenn Sie nur Elemente aus einem Array eliminieren, können Sie auch
filter()
verwenden.Suchen nach Elementen oder Testen, ob Elemente eine Bedingung erfüllen. Verwenden Sie
find()
undfindIndex()
, odersome()
undevery()
stattdessen. Diese Methoden haben den zusätzlichen Vorteil, dass sie zurückgeben, sobald das Ergebnis sicher ist, ohne das komplette Array zu durchlaufen.jsconst allEven = array.reduce((acc, cur) => acc && cur % 2 === 0, true);
jsconst allEven = array.every((val) => val % 2 === 0);
In Fällen, in denenreduce()
die beste Wahl ist, können Dokumentationen und semantische Variablennamen helfen, Lesbarkeitsnachteile zu minimieren.
Spezifikationen
Specification |
---|
ECMAScript® 2026 Language Specification> # sec-array.prototype.reduce> |
Browser-Kompatibilität
Loading…