che uso ha il metodo javascript forEach (quella mappa non può fare)?


90

L'unica differenza che vedo in map e foreach è che maprestituisce un array e forEachnon lo è. Tuttavia, non capisco nemmeno l'ultima riga del forEachmetodo " func.call(scope, this[i], i, this);". Ad esempio, non è " this" e " scope", riferendosi al medesimo oggetto e non è this[i]e ifacendo riferimento al valore di corrente nel circuito?

Ho notato che in un altro post qualcuno diceva "Usa forEachquando vuoi fare qualcosa sulla base di ogni elemento della lista. Potresti aggiungere cose alla pagina, per esempio. Essenzialmente, è ottimo per quando vuoi" effetti collaterali ". Non so cosa si intenda per effetti collaterali.

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

Infine, ci sono usi reali per questi metodi in javascript (poiché non stiamo aggiornando un database) oltre a manipolare numeri in questo modo:

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

Grazie per qualsiasi risposta.


Come si può trovare la definizione di una funzione di JavaScript nativo, come hai fatto con mape forEach? Tutto ciò che ottengo da Google sono le specifiche di utilizzo e i tutorial.
Woodrow Shigeru

Vedi anche il linguaggio agnostico C'è differenza tra foreach e map?
Bergi

Risposte:


94

La differenza essenziale tra mape forEachnel tuo esempio è che forEachopera sugli elementi dell'array originale, mentre mapcome risultato restituisce esplicitamente un nuovo array.

Con forEachstai intraprendendo qualche azione con - e opzionalmente cambiando - ogni elemento nell'array originale. Il forEachmetodo esegue la funzione che fornisci per ogni elemento, ma non restituisce nulla ( undefined). D'altra parte, mapattraversa l'array, applica una funzione a ciascun elemento ed emette il risultato come un nuovo array .

L '"effetto collaterale" di forEachè che l'array originale viene modificato. "Nessun effetto collaterale" mapsignifica che, nell'uso idiomatico, gli elementi dell'array originali non vengono modificati; il nuovo array è una mappatura uno a uno di ogni elemento nell'array originale - la trasformazione di mappatura è la funzione fornita.

Il fatto che non sia coinvolto alcun database non significa che non dovrai operare su strutture dati, che, dopotutto, è una delle essenze della programmazione in qualsiasi linguaggio. Per quanto riguarda la tua ultima domanda, il tuo array può contenere non solo numeri, ma oggetti, stringhe, funzioni, ecc.


4
nota dal futuro: questa risposta è in realtà una sciocchezza; .mappuò fare tutto ciò che .forEachpuò fare (inclusa la modifica di elementi), restituisce semplicemente un nuovo elenco costruito dalla funzione iteratore.
Travis Webb

6
Rispondere dal passato: rispettosamente non sono d'accordo. .map()crea un nuovo array e non altera l'originale. Si potrebbe effettivamente modificare l'array che viene mappato, ma sarebbe per lo meno non idiomatico, se non privo di senso. .forEach(), sebbene simile, applica la sua funzione a ciascun elemento, ma restituisce sempre undefined. Nonostante tutto quanto sopra, l'OP chiede anche delle sue funzioni specifiche aggiunte al prototipo dell'array; non c'era ES5 qui in passato.
Ken Redler

4
Non c'è ancora niente che forEachfa che tu non possa fare con map. Potresti semplicemente non preoccuparti del valore restituito da mape avresti un file forEach. La differenza è che è super inefficace da utilizzare mapper attività in cui non si desidera creare un nuovo array in base ai risultati. Quindi per quelli, usi forEach.
colpisci il

2
@poke: ..e allo stesso modo, potresti fare tutto ciò di cui hai bisogno con un semplice vecchio forloop. Non sono d'accordo sul fatto che ci sia qualche differenza di "efficacia", ma non credo che nessuno sostenga che uno abbia una magia che l'altro non può in qualche modo ottenere. Sebbene ci sia, in effetti, una cosa che mapnon si può fare: non restituire un array. C'è valore nel dover JS all'altezza delle aspettative da altre lingue, come per il comportamento dei favoriti funzionali come map, reduce, filter, ecc Ad esempio, è possibile implementare reduceRightutilizzando blocchi simili, ma perché non basta usare reduceRight?
Ken Redler

1
@ChinotoVokro, il tuo esempio sta mutando esplicitamente l'array originale assegnandolo b.val = b.val**2 all'interno dell'oggetto restituito . Questa è la cosa precisa che stiamo dicendo che è effettivamente possibile ma confusa e non idiomatica. Normalmente restituisci semplicemente il nuovo valore, non lo assegni anche all'array originale:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Ken Redler

66

La differenza principale tra i due metodi è concettuale e stilistica: la usi forEachquando vuoi fare qualcosa su o con ogni elemento di un array (fare "con" è ciò che il post che citi per "effetti collaterali", credo) , mentre si utilizza mapquando si desidera copiare e trasformare ogni elemento di un array (senza modificare l'originale).

Poiché sia mape forEachchiamano una funzione su ogni elemento in un array, e quella funzione è definita dall'utente, non c'è quasi nulla che puoi fare con uno e non con l'altro. È possibile, sebbene brutto, usarlo mapper modificare un array sul posto e / o fare qualcosa con gli elementi dell'array:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

ma molto più chiaro e più ovvio riguardo alla tua intenzione di usare forEach:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

Soprattutto se, come di solito nel mondo reale, elè una variabile facilmente leggibile dall'uomo:

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

Allo stesso modo, puoi facilmente usare forEachper creare un nuovo array:

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

ma è più pulito da usare map:

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

Nota anche che, poiché mapcrea un nuovo array, probabilmente incorre almeno in un po 'di prestazioni / memoria quando tutto ciò di cui hai bisogno è l'iterazione, in particolare per gli array di grandi dimensioni - vedi http://jsperf.com/map-foreach

Per quanto riguarda il motivo per cui si desidera utilizzare queste funzioni, sono utili ogni volta che è necessario eseguire la manipolazione di array in javascript, che (anche se stiamo parlando solo di javascript in un ambiente browser) è abbastanza spesso, quasi in qualsiasi momento stai accedendo a un array che non stai annotando a mano nel tuo codice. Potresti avere a che fare con un array di elementi DOM sulla pagina, o dati estratti da una richiesta AJAX, o dati inseriti in un modulo dall'utente. Un esempio comune in cui mi imbatto è l'estrazione di dati da un'API esterna, dove potresti voler utilizzare mapper trasformare i dati nel formato che desideri e quindi utilizzare forEachper iterare sul tuo nuovo array per visualizzarlo all'utente.


vedo persone che usano .map()tutto il tempo per modificare gli elementi in linea. Avevo l'impressione che fosse il principale vantaggio di usare .mapover.forEach
chovy

1
@chovy Credo che dovresti scegliere in base al fatto che desideri ottenere una serie di risultati. .mappuò modificare elementi, sì, ma creerebbe un array che non ti serve. Dovresti anche considerare come la scelta dell'uno o dell'altro fa indovinare al lettore se hai o meno effetti collaterali
leewz

16

La risposta votata (da Ken Redler) è fuorviante.

UN effetto collaterale nell'informatica significa che una proprietà di una funzione / metodo altera uno stato globale [wiki] . In un certo senso stretto, questo può anche includere la lettura da uno stato globale, piuttosto che da argomenti. Nella programmazione imperativa o OO, gli effetti collaterali compaiono la maggior parte del tempo. E probabilmente lo stai usando senza rendertene conto.

La differenza significativa tra forEache mapè quellamap alloca memoria e memorizza il valore restituito, mentre lo forEachgetta via. Vedi le specifiche emca per maggiori informazioni.

Per quanto riguarda il motivo per cui la gente dice forEach è usato quando si desidera un effetto collaterale è che il valore di ritorno di forEachè sempre undefined. Se non ha alcun effetto collaterale (non cambia lo stato globale), la funzione sta solo sprecando tempo della CPU. Un compilatore ottimizzato eliminerà questo blocco di codice e lo sostituirà con il valore finale ( undefined).

A proposito, va notato che JavaScript non ha restrizioni sugli effetti collaterali. È ancora possibile modificare l'array originale all'interno map.

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]

1
Questa domanda e le sue risposte e i commenti sono sempre divertenti da tornare indietro ogni due anni. Il modo in cui la mappa è implementata in JS, in termini di allocazione della memoria, è un altro bel punto di vista.
Ken Redler

6

Questa è una bellissima domanda con una risposta inaspettata.

Quanto segue si basa sulla descrizione ufficiale diArray.prototype.map() .

Non c'è niente che forEach()possa fare che map()non possa. Cioè, map()è un rigoroso superinsieme di forEach().

Anche se map()di solito è usato per creare un nuovo array, esso può anche essere utilizzato per modificare la matrice corrente. Il seguente esempio lo illustra:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

Nell'esempio sopra, è astato convenientemente impostato in modo tale che a[i] === iper i < a.length. Anche così, dimostra la potenza di map(), e in particolare la sua capacità di cambiare l'array su cui è chiamato.

Nota 1:
la descrizione ufficiale implica che map()può anche cambiare la lunghezza dell'array su cui è chiamato! Tuttavia, non riesco a vedere (una buona) ragione per farlo.

Nota 2:
sebbene map()map sia un superinsieme di forEach(), forEach()dovrebbe comunque essere usato dove si desidera modificare un dato array. Questo rende chiare le tue intenzioni.

Spero che questo abbia aiutato.


8
In realtà c'è una cosa che forEach può fare che la mappa non può fare - non restituire un array.
Antti Haapala

1
Puoi anche utilizzare il terzo argomento della funzione di mappatura per modificare l'array di destinazione, invece della variabile con ambito:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926

@ pdoherty926 true, ma lo è anche possibile a.forEach(function(x, i, arr) { ..., il che, ancora una volta, è un modo concettualmente più corretto quando c'è l'intenzione di mutare ogni elemento originale invece di mappare i valori da un array a un altro
conny

@conny: d'accordo. Inoltre, vedere questa risposta , a una domanda simile.
Sumukh Barve,

3

Puoi usare mapcome se fosse forEach.

Tuttavia, farà più del necessario.

scopepuò essere un oggetto arbitrario; non è affatto necessariamente this.

Per quanto riguarda se ci sono usi reali per mape forEach, anche chiedere se ci sono usi reali per foro whileloop.


Ma perché qui vengono chiamati sia "scope" che "this": func.call (scope, this [i], i, this); Lo scope non è un parametro uguale all'oggetto corrente, che è "questo"?
JohnMerlino

No, può essere uguale all'oggetto corrente. L'oggetto stesso viene passato come terzo parametro all'array. scope = scope || thissignifica "se scopeè falso (undefined, null, false, ecc.) imposta l'ambito su thisinvece e continua".
wombleton

Puoi collegarmi a un esempio quando non è uguale a questo?
JohnMerlino

developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… ne ha uno in "Stampa del contenuto di un array con un metodo oggetto"
wombleton

1

Sebbene tutte le domande precedenti siano corrette, farei sicuramente una distinzione diversa. L'uso di mape forEachpuò implicare l'intento.

Mi piace usare map quando sto semplicemente trasformando i dati esistenti in qualche modo (ma voglio assicurarmi che i dati originali siano invariati.

Mi piace usare forEach quando modifico la raccolta in posizione.

Per esempio,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

La mia regola pratica è assicurarmi quando mapcrei sempre un nuovo oggetto / valore da restituire per ogni elemento dell'elenco di origine e restituisci piuttosto che eseguire semplicemente alcune operazioni su ciascun elemento.

A meno che tu non abbia una reale necessità di modificare l'elenco esistente, non ha davvero senso modificarlo sul posto e si adatta meglio a stili di programmazione funzionali / immutabili.


1

TL; Risposta DR -

map restituisce sempre un altro array.

forEach no. Sta a te decidere cosa fare. Restituisci un array se vuoi o fai qualcos'altro se non lo fai.

La flessibilità è auspicabile in determinate situazioni. Se non è per quello con cui hai a che fare, usa map.


0

Ancora una volta, mi sento come un negromante che aggiunge una risposta a una domanda che è stata posta 5 anni fa (fortunatamente c'è un'attività piuttosto recente, quindi non sono l'unico a disturbare i morti :).

Altri hanno già pubblicato la tua domanda principale sulla differenza tra le funzioni. Ma per...

ci sono usi reali per questi metodi in javascript (dal momento che non stiamo aggiornando un database) oltre a manipolare numeri in questo modo:

... è divertente che dovresti chiedere. Proprio oggi ho scritto un pezzo di codice che assegna un numero di valori da un'espressione regolare a più variabili utilizzando map per la trasformazione. È stato utilizzato per convertire una struttura basata su testo molto complicata in dati visualizzabili ... ma per semplicità, offrirò un esempio usando le stringhe di data, perché quelle sono probabilmente più familiari a tutti (anche se, se il mio problema fosse stato effettivamente con le date , invece di map avrei usato l'oggetto Date, che avrebbe svolto il lavoro magnificamente da solo).

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

Quindi ... anche se questo era solo un esempio - e ha fatto solo una trasformazione molto semplice per i dati (solo per amore di esempio) ... averlo fatto senza mappa sarebbe stato un compito molto più noioso.

Certo, è scritto in una versione di JavaScript che non penso che molti browser supportino ancora (almeno completamente) ma - ci stiamo arrivando. Se avessi bisogno di eseguirlo nel browser, credo che si trasporterebbe bene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.