for array javascript arrays foreach ecmascript-5 nodelist

array - ¿Qué hace[].forEach.call() en JavaScript?



iterate array javascript (8)

Estaba viendo algunos fragmentos de código, y encontré varios elementos llamando a una función sobre una lista de nodos con un forEach aplicado a una matriz vacía.

Por ejemplo, tengo algo así como:

[].forEach.call( document.querySelectorAll(''a''), function(el) { // whatever with the current node });

pero no puedo entender cómo funciona ¿Alguien puede explicarme el comportamiento del conjunto vacío frente a forEach y cómo funciona la call ?


El método querySelectorAll devuelve una NodeList , que es similar a una matriz, pero no es una matriz. Por lo tanto, no tiene un método forEach (qué objetos de matriz heredan a través de Array.prototype ).

Como una NodeList es similar a una matriz, los métodos de matriz realmente funcionarán en ella, por lo que al usar [].forEach.call está invocando el método Array.prototype.forEach en el contexto de NodeList , como si hubiera sido capaz de simplemente haga yourNodeList.forEach(/*...*/) .

Tenga en cuenta que el literal vacío de la matriz es solo un acceso directo a la versión expandida, que probablemente verá muy a menudo también:

Array.prototype.forEach.call(/*...*/);


Las otras respuestas explicaron este código muy bien, así que solo agregaré una sugerencia.

Este es un buen ejemplo de código que debe ser refactorizado por simplicidad y claridad. En lugar de usar [].forEach.call() o Array.prototype.forEach.call() cada vez que hace esto, realice una función simple a partir de él:

function forEach( list, callback ) { Array.prototype.forEach.call( list, callback ); }

Ahora puede llamar a esta función en lugar del código más complicado y oscuro:

forEach( document.querySelectorAll(''a''), function( el ) { // whatever with the current node });


Se puede escribir mejor usando

Array.prototype.forEach.call( document.querySelectorAll(''a''), function(el) { });

Lo que hace es document.querySelectorAll(''a'') devuelve un objeto similar a una matriz, pero no hereda del tipo Array . Entonces llamamos al método forEach del objeto Array.prototype con el contexto como el valor devuelto por document.querySelectorAll(''a'')


Solo agrega una línea:

NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;

¡Y voilá!

document.querySelectorAll(''a'').forEach(function(el) { // whatever with the current node });

Disfruta :-)

Advertencia: NodeList es una clase global. No use esta recomendación si está escribiendo en una biblioteca pública. Sin embargo, es una forma muy conveniente de aumentar la autoeficacia cuando trabajas en el sitio web o la aplicación node.js.


Una matriz vacía tiene una propiedad para forEach en su prototipo que es un objeto Function. (La matriz vacía es solo una manera fácil de obtener una referencia a la función forEach que tienen todos los objetos Array ). Los objetos Function, a su vez, tienen una propiedad de call que también es una función. Cuando invocas la función de call una Función, ejecuta la función con los argumentos dados. El primer argumento se convierte en this en la función llamada.

Puede encontrar documentación para la función de call here . La documentación para forEach está here .


[] es una matriz.
Esta matriz no se usa en absoluto.

Se está poniendo en la página, porque el uso de una matriz le da acceso a prototipos de matriz, como .forEach .

Esto es más rápido que escribir Array.prototype.forEach.call(...);

A continuación, forEach es una función que toma una función como entrada ...

[1,2,3].forEach(function (num) { console.log(num); });

... y para cada elemento en this (donde this es similar a una matriz, en el sentido de que tiene una length y puede acceder a sus partes de this[1] ), pasará tres cosas:

  1. el elemento en la matriz
  2. el índice del elemento (el tercer elemento pasaría 2 )
  3. una referencia a la matriz

Por último, .call es un prototipo que las funciones tienen (es una función que se llama en otras funciones).
.call tomará su primer argumento y lo reemplazará dentro de la función normal con lo que sea que haya pasado, ya que el primer argumento ( undefined o null usará window en JS cotidianos, o será lo que sea que haya pasado, si está en "modo estricto" ) El resto de los argumentos se pasarán a la función original.

[1, 2, 3].forEach.call(["a", "b", "c"], function (item, i, arr) { console.log(i + ": " + item); }); // 0: "a" // 1: "b" // 2: "c"

Por lo tanto, está creando una forma rápida de llamar a la función forEach , y está cambiando this de la matriz vacía a una lista de todas las etiquetas <a> , y para cada <a> orden, está llamando a la función previsto.

EDITAR

Conclusión lógica / Limpieza

A continuación, hay un enlace a un artículo que sugiere que desechamos los intentos de programación funcional, y nos atenemos al bucle en línea manual, todo el tiempo, porque esta solución es hack-ish y antiestética.

Yo diría que si bien .forEach es menos útil que sus contrapartes, .map(transformer) , .filter(predicate) , .reduce(combiner, initialValue) , todavía sirve para fines cuando todo lo que realmente quiere hacer es modificar el exterior mundo (no la matriz), n veces, al tener acceso a arr[i] o i .

Entonces, ¿cómo lidiamos con la disparidad, ya que Motto es claramente un tipo talentoso y conocedor, y me gustaría imaginar que sé lo que estoy haciendo / dónde voy (de vez en cuando ... ... otra persona). veces es el aprendizaje de cabeza)

La respuesta es bastante simple, y algo que tío Bob y Sir Crockford enfrentarían a ambos, debido a la supervisión:

limpiarlo .

function toArray (arrLike) { // or asArray(), or array(), or *whatever* return [].slice.call(arrLike); } var checked = toArray(checkboxes).filter(isChecked); checked.forEach(listValues);

Ahora, si está cuestionando si necesita hacer esto usted mismo, la respuesta puede ser no ...
Esto exactamente lo hace ... ... cada biblioteca (?) Con características de orden superior en estos días.
Si está usando lodash o underscore o incluso jQuery, todos tendrán una forma de tomar un conjunto de elementos y realizar una acción n veces.
Si no está usando tal cosa, entonces por supuesto, escriba la suya.

lib.array = (arrLike, start, end) => [].slice.call(arrLike, start, end); lib.extend = function (subject) { var others = lib.array(arguments, 1); return others.reduce(appendKeys, subject); };

Actualización para ES6 (ES2015) y más allá

No solo el método slice( ) / array( ) / etc helper va a hacer la vida más fácil para las personas que desean usar listas tal como usan matrices (como deberían), sino para las personas que tienen el lujo de operar en ES6 + navegadores de un futuro relativamente cercano, o de "transpiling" en Babel hoy, tiene características de lenguaje integradas, que hacen innecesario este tipo de cosas.

function countArgs (...allArgs) { return allArgs.length; } function logArgs (...allArgs) { return allArgs.forEach(arg => console.log(arg)); } function extend (subject, ...others) { /* return ... */ } var nodeArray = [ ...nodeList1, ...nodeList2 ];

Súper limpio, y muy útil.
Busque los operadores de Descanso y Extensión ; pruébelos en el sitio de BabelJS; si su stack tecnológico está en orden, utilícelos en producción con Babel y un paso de compilación.

No hay una buena razón para no poder usar la transformación de una matriz que no sea una matriz a otra ... ... solo que no arruines tu código haciendo nada más que pegar esa misma línea fea, en todas partes.


[] siempre devuelve una nueva matriz, es equivalente a una new Array() pero se garantiza que devolverá una matriz porque el usuario puede sobrescribir la Array mientras que [] no puede. Entonces, esta es una forma segura de obtener el prototipo de Array ; luego, como se describe, la call se usa para ejecutar la función en la lista de nodos en forma de arrays (esto).

Llama a una función con un valor dado dado y argumentos provistos individualmente. mdn


Norguard explicó QUÉ [].forEach.call() hace y James Allardice POR QUÉ lo hacemos: porque querySelectorAll devuelve un NodeList que no tiene un método forEach ...

A menos que tenga un navegador moderno como Chrome 51+, Firefox 50+, Opera 38, Safari 10.

Si no, puede agregar un Polyfill :

if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = function (callback, thisArg) { thisArg = thisArg || window; for (var i = 0; i < this.length; i++) { callback.call(thisArg, this[i], i, this); } }; }