Ist es möglich, die Werte des Arrays zu ändern, wenn foreach in Javascript ausgeführt wird?


300

Beispiel:

var arr = ["one","two","three"];

arr.forEach(function(part){
  part = "four";
  return "four";
})

alert(arr);

Das Array hat immer noch seine ursprünglichen Werte. Gibt es eine Möglichkeit, über die Iterationsfunktion Schreibzugriff auf die Elemente des Arrays zu erhalten?



Probieren Sie die Karte aus ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ):x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]
Justin

Antworten:


468

Dem Rückruf werden das Element, der Index und das Array selbst übergeben.

arr.forEach(function(part, index, theArray) {
  theArray[index] = "hello world";
});

Bearbeiten - Wie in einem Kommentar angegeben, kann die .forEach()Funktion ein zweites Argument annehmen, das als Wert für thisjeden Aufruf des Rückrufs verwendet wird:

arr.forEach(function(part, index) {
  this[index] = "hello world";
}, arr); // use arr as this

Dieses zweite Beispiel zeigt arrsich wie thisim Rückruf eingerichtet. Man könnte denken, dass das am .forEach()Aufruf beteiligte Array der Standardwert von ist this, aber aus welchem ​​Grund auch immer; thiswird sein, undefinedwenn dieses zweite Argument nicht angegeben wird.

(Hinweis: Die obigen Informationen thisgelten nicht, wenn der Rückruf eine =>Funktion ist, da thissie beim Aufrufen solcher Funktionen niemals an irgendetwas gebunden sind.)

Es ist auch wichtig, sich daran zu erinnern, dass der Array-Prototyp eine ganze Familie ähnlicher Dienstprogramme enthält und im Stackoverflow viele Fragen zu der einen oder anderen Funktion auftauchen, sodass die beste Lösung darin besteht, einfach ein anderes Tool auszuwählen. Du hast:

  • forEach um mit oder mit jedem Eintrag in einem Array etwas zu tun;
  • filter zum Erzeugen eines neuen Arrays, das nur qualifizierende Einträge enthält;
  • map zum Erstellen eines neuen Eins-zu-Eins-Arrays durch Transformieren eines vorhandenen Arrays;
  • some um zu überprüfen, ob mindestens ein Element in einem Array zu einer Beschreibung passt;
  • everyum zu überprüfen, ob alle Einträge in einem Array mit einer Beschreibung übereinstimmen;
  • find um nach einem Wert in einem Array zu suchen

und so weiter. MDN-Link


34
Vielen Dank! ES6: arr.forEach ((o, i, a) => a [i] = myNewVal)
DiegoRBaquero

warum part(oder sogar oin es6) ist undefiniert? Wie erhalte ich einen iterierten Wert?
Daniil Mashkin

@DaniilMashkin partwäre, undefinedwenn ein Element des Arrays undefinedexplizit zugewiesen wurde . "Leere" Slots eines Arrays (ein Array-Eintrag, dem noch kein Wert zugewiesen wurde) werden von den .forEach()meisten anderen Array-Iterationsmethoden übersprungen .
Pointy

2
Der Vollständigkeit halber wird .forEach()auch ein zweites Argument verwendet thisArg, das Sie thisim Rückruf verwenden können. HINWEIS: Dies ist ein Argument von .forEachNICHT ein Argument des Rückrufs.
Moha das allmächtige Kamel

3
Um thisdas als zweites Argument übergebene Argument zu verwenden .forEach(), müssen Sie die Rückruffunktion mithilfe der Syntax übergeben function(), da die Verwendung der Pfeilfunktion von ES6 den () => {}Kontext nicht bindet.
Jpenna

131

Lassen Sie uns versuchen , es einfach zu halten und zu diskutieren, wie es tatsächlich funktioniert. Es hat mit Variablentypen und Funktionsparametern zu tun.

Hier ist Ihr Code, über den wir sprechen:

var arr = ["one","two","three"];

arr.forEach(function(part) {
  part = "four";
  return "four";
})

alert(arr);

Zunächst sollten Sie hier etwas über Array.prototype.forEach () lesen:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Lassen Sie uns zweitens kurz über Werttypen in JavaScript sprechen.

Grundelemente (undefiniert, null, Zeichenfolge, Boolescher Wert, Zahl) speichern einen tatsächlichen Wert.

Ex: var x = 5;

Referenztypen (benutzerdefinierte Objekte) speichern den Speicherort des Objekts.

Ex: var xObj = { x : 5 };

Und drittens, wie Funktionsparameter funktionieren.

In Funktionen werden Parameter immer als Wert übergeben.

Da arres sich um ein Array von Strings handelt, handelt es sich um ein Array primitiver Objekte, dh sie werden nach Wert gespeichert.

Für Ihren obigen Code bedeutet dies, dass jedes Mal, wenn forEach () iteriert, partderselbe Wert wie arr[index], aber nicht dasselbe Objekt ist .

part = "four";ändert die partVariable, lässt sie aber in arrRuhe.

Der folgende Code ändert die gewünschten Werte:

var arr = ["one","two","three"];

arr.forEach(function(part, index) {
  arr[index] = "four";
});

alert(arr);

Wenn das Array arrnun ein Array von Referenztypen war , funktioniert der folgende Code, da Referenztypen einen Speicherort eines Objekts anstelle des tatsächlichen Objekts speichern.

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // part and arr[index] point to the same object
  // so changing the object that part points to changes the object that arr[index] points to

  part.num = "four";
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

Das Folgende zeigt, dass Sie ändern können part, um auf ein neues Objekt zu zeigen, während Sie die Objekte in arrRuhe lassen:

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // the following will not change the object that arr[index] points to because part now points at a new object
  part = 5;
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

2
in der Tat eine großartige Erklärung! Es wäre noch besser gewesen, die Erklärung bei den anderen Iterationsmethoden für ... von, map, ... zu erweitern. Das neue für ... von funktioniert in solchen Fällen auf sehr ähnliche Weise wie für jedes, außer dass das " index "fehlt, um eventuell das ursprüngliche Array zu ändern (wie in forEach). Auch die ES5-Map scheint forEach ähnlich zu sein, vielleicht nur, dass die Möglichkeit, Werte von einer Map zurückzugeben, die Dinge aus syntaktischer Sicht klarer macht als die Verwendung des Index.
Christophe Vidal

1
Tolle Erklärung. Danke dir. Immerhin kann ich diese Aussage nicht verstehen: In functions, parameters are always passed by value. Was ist mit dem zweiten Beispiel?
Alex

@ 7sides, Diese Anweisung beschreibt, dass Funktionsparameter immer als Wert übergeben werden. Für Primitive ist es also der Wert, auf den das Primitiv zeigt. Bei Objekten ist dies der Ort, auf den das Objekt zeigt. Diese w3schools Seite hat eine gute Erklärung. Siehe Abschnitte Argumente werden als Wert übergeben und Objekte werden als Referenz übergeben .
Dave

In Funktionen werden Parameter immer als Wert übergeben. BAM. Danke. +1
Radarbob

92

Array: [1, 2, 3, 4]
Ergebnis:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Behalten Sie das ursprüngliche Array

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
const modifiedArr = originalArr.map(name => `${name}man`);

console.log( "Original: %s", originalArr );
console.log( "Modified: %s", modifiedArr );

Array.prototype.forEach() Originalarray überschreiben

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);

console.log( "Overridden: %s", originalArr );


3
"Array.prototype.map () Ändern Sie das ursprüngliche Array, indem Sie die arr-Variable neu zuweisen." .Map () ändert niemals das ursprüngliche Array, sondern erstellt ein neues. Durch die Neuzuweisung der Variablen wird das ursprüngliche Objekt nicht geändert.
Vadkou

1
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map () Ändern Sie niemals das ursprüngliche Array, für jede Mutation.
Anupam Maurya

@ AnupamMaurya Das stimmt überhaupt nicht. mapkann definitiv sein Array mutieren und forEachim Allgemeinen nicht.
user4642212

@SebastianSimon, danke für die Antwort. Alles, was ich hinzufügen möchte, forEach überschreibt die Daten, aber eine Karte kann nicht, es erstellt eine neue Kopie.
Anupam Maurya

14

Javascript wird als Wert übergeben und bedeutet im Wesentlichen, dass partes sich um eine Kopie handelt des Werts im Array handelt.

Um den Wert zu ändern, greifen Sie auf das Array selbst in Ihrer Schleife zu.

arr[index] = 'new value';


Es hängt von der Art ab, partob es kopiert wird - obwohl Sie Recht haben, dass die Variable kein Zeiger, sondern ein Wert ist
Bergi

8
Die Aussage "JavaScript wird als Wert übergeben" ist eine grobe Verallgemeinerung. Es gibt Referenztypen in JavaScript. Werttypen werden als Wert übergeben.
Alex Turpin

9
Keine grobe Verallgemeinerung. Eine völlig korrekte und absolute Aussage. Javascript übergibt Referenzen nach Wert. Javascript wird immer als Wert übergeben.
Hvgotcodes

1
@Bergi: Nein, es spielt keine Rolle, welcher Typ. Alle Werte werden bei der Zuweisung kopiert - die einzigen Werte in JavaScript sind Grundelemente und Referenzen. Sowohl Grundelemente als auch Referenzen werden bei der Zuweisung kopiert.
Newacct

6
@Bergi Nur weil eine Sprache eine Referenz hat, heißt das nicht, dass sie Referenzübergabe verwendet. Referenzen können (und werden) als Wert übergeben, dh der Wert der Referenz wird kopiert und diese Kopie wird als Argument verwendet.
Hvgotcodes

4

Ersetzen Sie es durch den Index des Arrays.

array[index] = new_value;

2
Cool ... Immer eine Erklärung hinzufügen
devpro

4

Hier ist eine ähnliche Antwort unter Verwendung einer =>Stilfunktion:

var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );

gibt das Ergebnis:

[11,12,13,14]

Der selfParameter ist bei der Pfeilstilfunktion daher nicht unbedingt erforderlich

data.forEach( (item,i) => data[i] = item + 10);

funktioniert auch.


3

Die .forEach-Funktion kann eine Rückruffunktion haben (jedes Element, elementIndex). Grundsätzlich müssen Sie also Folgendes tun:

arr.forEach(function(element,index){
    arr[index] = "four";   //set the value  
});
console.log(arr); //the array has been overwritten.

Wenn Sie das ursprüngliche Array beibehalten möchten, können Sie eine Kopie davon erstellen, bevor Sie den obigen Vorgang ausführen. Um eine Kopie zu erstellen, können Sie Folgendes verwenden:

var copy = arr.slice();

3
Wenn Sie eine Kopie des Arrays erstellen möchten, verwenden Sie map()anstelle von forEach(). map()iteriert über das Quellarray und gibt ein neues Array zurück, das die [modifizierte] Kopie des Originals enthält: Das Quellarray bleibt unverändert.
Nicholas Carey

2

Mit den Array-Objektmethoden können Sie den Array-Inhalt ändern, aber im Vergleich zu den grundlegenden for-Schleifen fehlt diesen Methoden eine wichtige Funktionalität. Sie können den Index während des Laufs nicht ändern.

Wenn Sie beispielsweise das aktuelle Element entfernen und an einer anderen Indexposition innerhalb desselben Arrays platzieren, können Sie dies problemlos tun. Wenn Sie das aktuelle Element an eine vorherige Position verschieben, gibt es in der nächsten Iteration kein Problem. Sie erhalten das gleiche nächste Element, als hätten Sie nichts getan.

Betrachten Sie diesen Code, in dem wir das Element an Indexposition 5 auf Indexposition 2 verschieben, sobald der Index bis zu 5 zählt.

var ar = [0,1,2,3,4,5,6,7,8,9];
ar.forEach((e,i,a) => {
i == 5 && a.splice(2,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 6 - 7 7 - 8 8 - 9 9

Wenn wir das aktuelle Element jedoch an einen Ort jenseits der aktuellen Indexposition verschieben, wird es etwas chaotisch. Dann verschiebt sich das nächste Element in die Position der verschobenen Elemente und in der nächsten Iteration können wir es nicht sehen oder bewerten.

Betrachten Sie diesen Code, in dem wir das Element an Indexposition 5 auf Indexposition 7 verschieben, sobald der Index bis zu 5 zählt.

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && a.splice(7,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 7 - 7 5 - 8 8 - 9 9

Wir haben also noch nie 6 in der Schleife getroffen. Normalerweise wird in einer for-Schleife erwartet, dass Sie den Indexwert verringern, wenn Sie das Array-Element nach vorne verschieben, sodass Ihr Index im nächsten Durchlauf an derselben Position bleibt und Sie das an der Stelle des entfernten Elements verschobene Element weiterhin auswerten können. Dies ist mit Array-Methoden nicht möglich. Sie können den Index nicht ändern. Überprüfen Sie den folgenden Code

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && (a.splice(7,0,a.splice(i,1)[0]), i--);
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 4 5 - 6 7 - 7 5 - 8 8 - 9 9

Wie Sie sehen, wird es beim Dekrementieren inicht von 5 auf 6 fortgesetzt, von wo es übrig geblieben ist.

Denken Sie also daran.


1

Um Elemente hinzuzufügen oder zu löschen, die den Index ändern würden, zählen Sie einfach die Anzahl der Elemente, die Sie bereits gelöscht oder hinzugefügt haben, und ändern Sie den Index entsprechend, um den Vorschlag von zhujy_8833 zu erweitern. Zum Löschen von Elementen:

let values = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"];
let count = 0;
values.slice().forEach((value, index) => {
    if (value === "A2" || value === "A5") {
        values.splice(index - count++, 1);
    };
});
console.log(values);

// Expected: [ 'A0', 'A1', 'A3', 'A4', 'A6', 'A7', 'A8' ]

So fügen Sie Elemente vor:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - count--, 0, 'newVal');
};

// Expected: ['newVal', A0, 'A1', 'A2', 'A3', 'A4', 'A5', 'newVal', 'A6', 'A7', 'newVal', 'A8' ]

So fügen Sie Elemente nach:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - --count, 0, 'newVal');
};

// Expected: ['A0', 'newVal', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'newVal', 'A7', 'A8', 'newVal']

So ersetzen Sie ein Element:

if (value === "A3" || value === "A4" || value === "A7") {
    values.splice(index, 1, 'newVal');
};

// Expected: [ 'A0', 'A1', 'A2', 'newVal', 'newVal', 'A5', 'A6', 'newVal', 'A8' ]

Hinweis: Wenn Sie sowohl 'vor' als auch 'nach' Einfügungen implementieren, sollte der Code zuerst 'vor' Einfügungen verarbeiten, anders herum wäre dies nicht wie erwartet


0

Sie können dies versuchen, wenn Sie überschreiben möchten

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.