Yo estaba mirando algunos fragmentos de código, y me encontré con varios elementos de llamar a una función a través de una lista de nodos con un forEach aplicada a una matriz vacía.

Por ejemplo yo tengo algo como:

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

pero no puedo entender cómo funciona. Puede que alguien me explique el comportamiento de la matriz vacía en frente de la forEach y cómo la call funciona?

InformationsquelleAutor Mimo | 2013-04-17

10 Comentarios

  1. 175

    [] es una matriz.

    Esta matriz no se usa en absoluto.

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

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

    Siguiente, forEach es una función que toma una función como una entrada…

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

    …y para cada elemento en this (donde this es la matriz de como, en la medida que tiene un length y usted puede tener acceso a sus partes, como this[1]) se va a pasar tres cosas:

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

    Por último, .call es un prototipo que funciones tiene (es una función que se llama en otras funciones).

    .call tendrá su primer argumento y reemplazar this dentro de la función regular, con lo que te pasa call, como el primer argumento (undefined o null utilizará window en la vida cotidiana de JS, o será lo que pasa, si en «estricto-mode»). El resto de los argumentos se pasan 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, usted puede crear una forma rápida de llamar a la forEach función, y vas a cambiar this de la matriz vacía a una lista de todos los <a> etiquetas, y para cada <a> en fin, se llama a la función.

    EDITAR

    Conclusión Lógica /Limpieza

    A continuación, hay un enlace a un artículo sugiriendo que la chatarra de los intentos en la programación funcional, y se adhieren a manual en línea en bucle, cada vez, ya que esta solución es hack-ish y antiestéticas.

    Yo diría que mientras .forEach es menos útil que el de sus homólogos, .map(transformer), .filter(predicate), .reduce(combiner, initialValue), todavía sirve a los efectos de cuando todo lo que realmente quiero hacer es modificar el mundo exterior (no la de la matriz), n-veces, a pesar de tener acceso a cualquiera de los arr[i] o i.

    Entonces, ¿cómo podemos lidiar con la desigualdad, como el Lema es claramente un talento y conocimiento chico, y me gustaría pensar que yo sé lo que estoy haciendo/a donde voy (ahora y entonces… …otras veces es primero la cabeza de aprendizaje)?

    La respuesta es en realidad bastante simple, y algo del Tío Bob y Sir Crockford ambos facepalm, debido a la supervisión:

    limpiar.

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

    Ahora, si usted se está preguntando si usted necesita para hacer esto, usted mismo, la respuesta puede ser no…

    Precisamente esto es hecho por… …todos(?) biblioteca de orden superior, las características de estos días.

    Si usted está usando lodash o subrayado o incluso jQuery, todos van a tener una manera de tomar un conjunto de elementos, y la realización de una acción n-veces.

    Si no vas a usar tal cosa, entonces por todos los medios, escribir su propio.

    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 sólo es un slice( )/array( )/etc método auxiliar que va a hacer la vida más fácil para las personas que quieren usar listas igual que el uso de matrices (como deberían), pero para las personas que tienen el lujo de operar en ES6+ exploradores de la relativamente cerca de futuro, o de «transpiling» en Babel hoy en día, usted tiene las características de lenguaje integrado, lo que hacen este tipo de cosas innecesarias.

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

    Super-limpio, y muy útil.

    Buscar el Resto y Propagación operadores; probarlos en el BabelJS sitio; si la tecnología de la pila está en orden, el uso en producción, con Babel y a un paso de compilación.


    No hay una buena razón para no ser capaz de utilizar la transformación de la matriz no en una matriz… …simplemente no hacer un lío de su código de no hacer nada pero pegar ese mismo feo línea, en todas partes.

    • Ahora estoy un poco confundida porque si yo lo hice: la consola.de registro(este); yo siempre obtendrá el objeto de la ventana Y pensé que .llamar cambia el valor de esta
    • .call no cambiar el valor de this, que es la razón por la que usted está utilizando call con el forEach. Si forEach parece forEach (fn) { var i = 0; var len = this.length; for (; i < length; i += 1) { fn( this[i], i, this ); }, a continuación, cambiando this diciendo forEach.call( newArrLike ) significa que va a utilizar el nuevo objeto como this.
    • Sí, pero aún si lo hice console.log(this) dentro de la función de la forEach que debo obtener la lista de nodos he añadido a través de .call porque he cambiado el valor de esto, sin embargo, lo que me confunde es que this es igual a la Ventana y no la lista de nodos
    • no cambia el valor de this dentro de lo que pase a forEach, .call cambios en el valor de this dentro de un forEach. Si usted está confundido en cuanto a por qué this no se pasan de forEach en la función que quería, usted debe buscar en el comportamiento de this en JavaScript. Como un apéndice, aplicable sólo aquí (y el mapa/filtro/reducir), hay un segundo argumento para forEach, que establece this dentro de la función arr.forEach(fn, thisInFn) o [].forEach.call(arrLike, fn, thisInFn); o, simplemente, utilizar .bind; arr.forEach(fn.bind(thisInFn));
    • puede usted por favor aclarar el significado de este «El resto de los argumentos se pasan a la función original.»? qué significa [1,2,3]? traté de consola.registro de arr y consiguió el primer argumento de la llamada es decir, [a,b,c], no [1,2,3]. así que parece tener cosas en la [1,2,3] matriz o no, no es realmente hacer una diferencia sí?
    • ese es exactamente el punto. [] es justo allí, como una matriz, de modo que usted puede .call la .forEach método, y cambiar el this para el conjunto que desea hacer un trabajo de. .call se parece a esto: function (thisArg, a, b, c) { var method = this; method(a, b, c); } excepto que no hay negro interno-magia para establecer this dentro de method a la igualdad de lo que pasa como thisArg.
    • Eso no es probable útil para usted, ahora mismo; así que vamos a intentarlo: function sum (a, b) { console.log(this.name); return a + b; } var obj = { name: "test" }; sum.call( obj, 1, 2 ); // logs "test", returns 3
    • así que, en resumen… de la Matriz.a partir de()?
    • o [...xs].forEach(f);. Ahora, por supuesto. No cuando pidió originalmente/respondidas.
    • sí, sólo estoy sorprendido de que algunas de las cosas básicas como iterar sobre la lista de nodos fueron aún más sucio que el de hoy
    • El DOM es muy desagradable, a pesar de que ha realizado una gran cantidad de avances en la última década. La expectativa sería que usted escribió un bucle for o un while loop, como cualquier buen Java/C programador. Lo mismo es cierto de la arguments lista dentro de las funciones; no es una matriz, es una lista. Para más for bucles es.
    • tbh soy un fan de la explícitas para los bucles como se puede ver claramente lo que está pasando, pero a veces se necesita una matriz para otra cosa además de bucle y, a continuación, se rompe, eso es lamentable

  2. 46

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

    Desde un NodeList es similar a una matriz, la matriz de métodos realmente va a funcionar, así que mediante el uso de [].forEach.call va a invocar la Array.prototype.forEach método en el contexto de la NodeList, como si hubiera sido capaz de simplemente hacer yourNodeList.forEach(/*...*/).

    Tenga en cuenta que la matriz vacía literal es sólo un acceso directo a la versión ampliada, lo cual probablemente verás muy a menudo demasiado:

    Array.prototype.forEach.call(/*...*/);
    • Así que, para aclarar, no hay ninguna ventaja en el uso de [].forEach.call(['a','b'], cb) más de ['a','b'].forEach(cb) en aplicaciones de uso diario con el estándar de matrices, justo cuando tratando de recorrer el array-como las estructuras que no tienen forEach en su propio prototipo? Es eso correcto?
    • Sí, eso es correcto. Ambos trabajarán pero, ¿por qué complicarse demasiado las cosas? Basta con llamar al método directamente en la propia matriz.
    • Genial, gracias. Y no sé por qué, tal vez sólo por la calle cred, impresionar a las mujeres de edad en ALDI y tal. Me quedo con [].forEach() 🙂
    • var section = document.querySelectorAll(".section"); section.forEach((item)=>{ console.log(item.offsetTop) }) funciona bien
  3. 20

    Las otras respuestas han explicado que este código muy bien, así que sólo voy a añadir una sugerencia.

    Este es un buen ejemplo de código que debe ser refactorizado para la simplicidad y la claridad. En lugar de utilizar [].forEach.call() o Array.prototype.forEach.call() cada vez que usted hace esto, hacer una simple función de ella:

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

    Ahora se puede llamar a esta función en lugar de la más complicada y oscura código:

    forEach( document.querySelectorAll('a'), function( el ) {
       //whatever with the current node
    });
    • El amor cómo legible esto es.
  4. 4

    Puede ser mejor escrito utilizando

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

    Lo que se hace es document.querySelectorAll('a') devuelve un objeto similar a una matriz, pero no se hereda de la Array tipo.
    Así que llama a la forEach método de la Array.prototype objeto con el contexto como el valor devuelto por document.querySelectorAll('a')

  5. 1

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

    Puede encontrar la documentación de la call función aquí. Documentación para forEach es aquí.

  6. 1

    Acaba de agregar una línea:

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

    Y voila!

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

    Disfrutar :—)

    Advertencia: lista de nodos es una clase global. No use esta recomendación si la escritura de la biblioteca pública. Sin embargo, es muy conveniente para el aumento de la auto-eficacia cuando se trabaja en el sitio web o node.js app.

    • No modificar los objetos que usted no es dueño de…
    • estás en lo correcto. Me han aclarado la respuesta.
  7. 1

    Sólo una rápida y sucia solución siempre termino usando. No me toque los prototipos, así como una buena práctica. Por supuesto, hay un montón de maneras de hacer esto mejor, pero usted consigue la idea.

    const forEach = (array, callback) => {
      if (!array || !array.length || !callback) return
      for (var i = 0; i < array.length; i++) {
        callback(array[i], i);
      }
    }
    
    forEach(document.querySelectorAll('.a-class'), (item, index) => {
      console.log(`Item: ${item}, index: ${index}`);
    });
  8. 0

    [] siempre devuelve una nueva matriz, es equivalente a new Array() sino que está garantizada para devolver un array porque Array podría ser sobrescrito por el usuario, mientras que [] no se puede. Así que esta es una forma segura de obtener el prototipo de Array, entonces como se describe, call se utiliza para ejecutar la función en el arraylike lista de nodos (este).

    Llama a una función con un determinado este valor y argumentos
    de forma individual. mdn

    • Mientras [] en el hecho de que siempre devuelven un array, mientras que window.Array se puede reemplazar con nada (en todos los navegadores antiguos), así también puede window.Array.prototype.forEach se sobrescribirán con nada. No hay ninguna garantía de seguridad en cualquiera de los casos, en los navegadores que no soportan getters/setters o el objeto de sellado/de la congelación (congelación causando sus propios problemas de extensibilidad).
    • Siéntase libre de editar la respuesta a agregar la información adicional con respecto a la posibilidad de sobrescribir el prototype o métodos: forEach.
  9. 0

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

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

    Si no puede agregar un El polyfil:

    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);
            }
        };
    }
  10. 0

    Desea actualizar en esta vieja pregunta:

    La razón para utilizar [].foreach.call() un bucle a través de los elementos en los navegadores modernos, es todo. Podemos utilizar document.querySelectorAll("a").foreach() directamente.

    Lista de nodos para los objetos son conjuntos de nodos, generalmente devueltos por
    propiedades tales como Nodo.childNodes y métodos tales como
    documento.querySelectorAll().

    Aunque la lista de nodos no es un Array, es posible iterar sobre ella
    con forEach()
    . También puede ser convertido a una Matriz real utilizando
    De la matriz.a partir de().

    Sin embargo, algunos navegadores antiguos no han implementado lista de nodos.forEach()
    ni de la Matriz.a partir de(). Esto puede evitarse usando
    De la matriz.el prototipo.forEach() — consulte este documento del Ejemplo.

Dejar respuesta

Please enter your comment!
Please enter your name here