¿Qué uso tiene el javascript para cada método (ese mapa no puede hacer)?

90

La única diferencia que veo en map y foreach es que mapdevuelve una matriz y forEachno. Sin embargo, ni siquiera entiendo la última línea del forEachmétodo " func.call(scope, this[i], i, this);". Por ejemplo, no es " this" y " scope", en referencia al mismo objeto y no es this[i], y ien referencia al valor de la corriente en el circuito?

Noté en otra publicación que alguien decía "Úselo forEachcuando quiera hacer algo basándose en cada elemento de la lista. Puede que esté agregando cosas a la página, por ejemplo. Esencialmente, es ideal para cuando quiere" efectos secundarios ". No sé qué se entiende por efectos secundarios.

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); 
    } 
}

Finalmente, ¿hay algún uso real para estos métodos en javascript (ya que no estamos actualizando una base de datos) además de manipular números como este?

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

Gracias por cualquier respuesta.

John Merlino
fuente
¿Cómo se puede encontrar la definición de JavaScript nativo de una función, como lo hizo con mapy forEach? Todo lo que obtengo de Google son especificaciones de uso y tutoriales.
WoodrowShigeru
Consulte también el idioma independiente. ¿Hay alguna diferencia entre foreach y map?
Bergi

Respuestas:

94

La diferencia esencial entre mapy forEachen su ejemplo es que forEachopera sobre los elementos originales de la matriz, mientras mapque como resultado devuelve explícitamente una nueva matriz.

Con forEachusted está tomando alguna acción con, y opcionalmente cambiando, cada elemento en la matriz original. El forEachmétodo ejecuta la función que proporcionas para cada elemento, pero no devuelve nada ( undefined). Por otro lado, maprecorre la matriz, aplica una función a cada elemento y emite el resultado como una nueva matriz .

El "efecto secundario" forEaches que se está cambiando la matriz original. "Sin efectos secundarios" con mapsignifica que, en el uso idiomático, los elementos originales de la matriz no se modifican; la nueva matriz es un mapeo uno a uno de cada elemento en el arreglo original - la transformación de mapeo es su función provista.

El hecho de que no haya una base de datos involucrada no significa que no tendrá que operar con estructuras de datos, que, después de todo, es una de las esencias de la programación en cualquier lenguaje. En cuanto a su última pregunta, su matriz puede contener no solo números, sino también objetos, cadenas, funciones, etc.

Ken Redler
fuente
4
nota del futuro: esta respuesta es en realidad una tontería; .mappuede hacer todo lo que .forEachpuede hacer (incluida la modificación de elementos), solo devuelve una nueva lista construida a partir de la función del iterador.
Travis Webb
6
Responder desde el pasado: respetuosamente no estoy de acuerdo. .map()crea una nueva matriz y no altera la original. De hecho, podría modificar la matriz que se está mapeando, pero eso sería al menos no idiomático, si no sin sentido. .forEach(), aunque similar, aplica su función a cada elemento, pero siempre devuelve undefined. A pesar de todo lo anterior, el OP también está preguntando sobre sus propias funciones específicas agregadas al prototipo de matriz; no había ES5 aquí en el pasado.
Ken Redler
4
Todavía no hay nada que forEachhaga que no puedas hacer map. Simplemente podría no importarle el valor de retorno de mapy tendría un forEach. La diferencia es que es muy ineficaz de usar mappara tareas en las que no desea crear una nueva matriz basada en los resultados. Entonces, para esos, usa forEach.
empuje el
2
@poke: ..y del mismo modo, puedes hacer lo que necesites con un simple forbucle antiguo . No estoy en desacuerdo con que haya alguna diferencia en la "eficacia", pero tampoco creo que nadie esté argumentando que uno tiene algo de magia que el otro no puede lograr de alguna manera. Aunque, de hecho, hay una cosa que mapno se puede hacer: no devolver una matriz. No hay valor en tener JS altura de las expectativas de otros idiomas como el comportamiento de los favoritos funcionales como map, reduce, filter, etc. Por ejemplo, se podría poner en práctica reduceRightel uso de bloques de construcción similares, pero por qué no sólo tiene que utilizar reduceRight?
Ken Redler
1
@ChinotoVokro, su ejemplo está mutando explícitamente la matriz original asignando b.val = b.val**2 dentro del objeto devuelto . Eso es exactamente lo que estamos diciendo que es posible, pero confuso y no idiomático. Normalmente, simplemente devuelve el nuevo valor, no lo asigna también a la matriz original:a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Ken Redler
66

La principal diferencia entre los dos métodos es conceptual y estilística: se utiliza forEachcuando se quiere hacer algo con o con cada elemento de una matriz (creo que hacer "con" es lo que la publicación que cita significaba con "efectos secundarios") , mientras que usas mapcuando quieres copiar y transformar cada elemento de una matriz (sin cambiar el original).

Debido a que ambos mapy forEachllaman a una función en cada elemento de una matriz, y esa función está definida por el usuario, no hay casi nada que pueda hacer con uno y no con el otro. Es posible, aunque feo, usarlo mappara modificar una matriz en el lugar y / o hacer algo con elementos de la matriz:

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 }]

pero mucho más limpio y más obvio en cuanto a su intención de usar forEach:

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

Especialmente si, como suele ser el caso en el mundo real, eles una variable útilmente legible por humanos:

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

De la misma manera, puede usar fácilmente forEachpara crear una nueva matriz:

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

pero es más limpio de usar map:

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

Tenga en cuenta también que, debido a que mapcrea una nueva matriz, es probable que incurre en al menos algún impacto de rendimiento / memoria cuando todo lo que necesita es iteración, particularmente para matrices grandes, consulte http://jsperf.com/map-foreach

En cuanto a por qué querría usar estas funciones, son útiles cada vez que necesita realizar una manipulación de matrices en javascript, que (incluso si solo estamos hablando de javascript en un entorno de navegador) es bastante frecuente, casi en cualquier momento está accediendo a una matriz que no está escribiendo a mano en su código. Es posible que esté tratando con una matriz de elementos DOM en la página, o datos extraídos de una solicitud AJAX, o datos ingresados ​​en un formulario por el usuario. Un ejemplo común con el que me encuentro es extraer datos de una API externa, donde es posible que desee usar mappara transformar los datos en el formato que desee y luego usar forEachpara iterar sobre su nueva matriz para mostrárselo a su usuario.

nrabinowitz
fuente
Veo gente que usa .map()todo el tiempo para modificar elementos en línea. Tenía la impresión de que era el principal beneficio de usar .mapover.forEach
chovy
1
@chovy Creo que debe elegir en función de si desea obtener una variedad de resultados. .mappuede modificar elementos, sí, pero crearía una matriz que no necesita. También debe considerar cómo la elección de uno u otro permite que el lector adivinar si usted tiene o no efectos secundarios
leewz
16

La respuesta votada (de Ken Redler) es engañosa.

Un efecto secundario en informática significa que una propiedad de una función / método altera un estado global [wiki] . En un sentido estricto, esto también puede incluir la lectura de un estado global, en lugar de argumentos. En la programación imperativa o OO, los efectos secundarios aparecen la mayor parte del tiempo. Y probablemente lo esté utilizando sin darse cuenta.

La diferencia significativa entre forEachy mapes que mapasigna memoria y almacena el valor de retorno, mientras que lo forEachdesecha. Ver especificaciones emca para obtener más información.

En cuanto a la razón por la que la gente dice que forEachse usa cuando desea un efecto secundario, es que el valor de retorno de forEaches siempre undefined. Si no tiene ningún efecto secundario (no cambia el estado global), entonces la función solo está perdiendo el tiempo de la CPU. Un compilador de optimización eliminará este bloque de código y lo reemplazará con el valor final (undefined ).

Por cierto, cabe señalar que JavaScript no tiene restricciones sobre los efectos secundarios. Todavía puede modificar la matriz original en el interior 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]
Jack Tang
fuente
1
Siempre es divertido recordar esta pregunta y sus respuestas y comentarios cada dos años. La forma en que se implementa el mapa en JS, en términos de asignación de memoria, es otro buen ángulo.
Ken Redler
6

Esta es una hermosa pregunta con una respuesta inesperada.

Lo siguiente se basa en la descripción oficial deArray.prototype.map() .

No hay nada que forEach()pueda hacer que map()no pueda. Es decir, map()es un superconjunto estricto de forEach().

Aunque map()generalmente se usa para crear una nueva matriz, también se puede usar para cambiar la matriz actual. El siguiente ejemplo lo ilustra:

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!!

En el ejemplo anterior, ase configuró convenientemente a[i] === ipara i < a.length. Aun así, demuestra el poder demap() y, en particular, su capacidad para cambiar la matriz en la que se llama.

Nota 1:
La descripción oficial implica que map()incluso puede cambiar la longitud de la matriz en la que se llama. Sin embargo, no veo (una buena) razón para hacer esto.

Nota 2:
Si bien el map()mapa es un superconjunto de forEach(), forEach()aún debe usarse cuando se desee el cambio de una matriz determinada. Esto aclara tus intenciones.

Espero que esto haya ayudado.

Sumukh Barve
fuente
8
En realidad, hay 1 cosa que forEach puede hacer que el mapa no puede hacer: no devolver una matriz.
Antti Haapala
1
También puede usar el tercer argumento de la función de mapeo para mutar la matriz de destino, en lugar de la variable de ámbito:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926
@ pdoherty926 es cierto, pero también puede hacerlo a.forEach(function(x, i, arr) { ..., lo cual, nuevamente, es una forma conceptualmente más correcta cuando existe la intención de mutar cada elemento original en lugar de asignar valores de una matriz a otra
conny
@conny: De acuerdo. Además, consulte esta respuesta a una pregunta similar.
Sumukh Barve
3

Puede usarlo mapcomo si lo fuera forEach.

Sin embargo, hará más de lo necesario.

scopepuede ser un objeto arbitrario; de ninguna manera es necesariamente this.

En cuanto a si hay usos reales para mapy forEach, también preguntar si hay usos reales para foro whilebucles.

wombleton
fuente
Pero, ¿por qué se llaman aquí tanto "scope" como "this": func.call (scope, this [i], i, this); ¿No es el alcance un parámetro que es igual al objeto actual, que es "esto"?
JohnMerlino
No, puede ser igual al objeto actual. El objeto en sí se pasa como tercer parámetro a la matriz. scope = scope || thissignifica "si scopees falso (indefinido, nulo, falso, etc.) establecer el alcance en en su thislugar y continuar".
wombleton
¿Puedes vincularme a un ejemplo cuando no sea igual a esto?
JohnMerlino
developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… tiene uno en "Imprimir el contenido de una matriz con un método de objeto"
wombleton
1

Si bien todas las preguntas anteriores son correctas, definitivamente haría una distinción diferente. El uso de mapy forEachpuede implicar intención.

Me gusta usar mapcuando simplemente estoy transformando los datos existentes de alguna manera (pero quiero asegurarme de que los datos originales no cambien.

Me gusta usar forEachcuando modifico la colección en su lugar.

Por ejemplo,

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 }]

Mi regla general es asegurarme de que mapsiempre está creando un nuevo objeto / valor para devolver para cada elemento de la lista de origen y devolverlo en lugar de simplemente realizar alguna operación en cada elemento.

A menos que tenga una necesidad real de modificar la lista existente, realmente no tiene sentido modificarla en su lugar y encaja mejor en estilos de programación funcionales / inmutables.

maschwenk
fuente
1

TL; respuesta DR -

map siempre devuelve otra matriz.

forEach no lo hace. Depende de usted decidir qué hace. Devuelva una matriz si lo desea o haga otra cosa si no lo desea.

La flexibilidad es deseable en determinadas situaciones. Si no es por lo que está tratando, use map.

charsi
fuente
0

Una vez más, me siento como un nigromante agregando una respuesta a una pregunta que se hizo hace 5 años (felizmente hay actividad bastante reciente, así que no soy el único que está molestando a los muertos :).

Otros ya han publicado sobre su pregunta principal sobre la diferencia entre las funciones. Pero para...

¿Hay algún uso real para estos métodos en javascript (ya que no estamos actualizando una base de datos) además de manipular números como este?

... es gracioso que preguntes. Hoy mismo escribí un fragmento de código que asigna una cantidad de valores de una expresión regular a múltiples variables usando map para la transformación. Se usó para convertir una estructura basada en texto muy complicada en datos visualizables ... pero en aras de la simplicidad, ofreceré un ejemplo usando cadenas de fecha, porque probablemente sean más familiares para todos (aunque, si mi problema hubiera sido realmente con las fechas , en lugar de map, habría usado Date-object, que habría hecho el trabajo espléndidamente por sí 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);

Entonces ... si bien esto fue solo un ejemplo, y solo hizo una transformación muy básica para los datos (solo por ejemplo) ... haber hecho esto sin mapa hubiera sido una tarea mucho más tediosa.

Por supuesto, está escrito en una versión de JavaScript que no creo que muchos navegadores admitan todavía (al menos completamente), pero estamos llegando allí. Si tuviera que ejecutarlo en el navegador, creo que se transpilaría muy bien.

Markku Uttula
fuente