O que .forEach.call () faz em JavaScript?

Eu estava olhando alguns trechos de código, e encontrei vários elementos chamando uma function sobre uma lista de nós com um forEach aplicado a uma matriz vazia.

Por exemplo, eu tenho algo como:

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

mas não consigo entender como funciona. Alguém pode me explicar o comportamento do array vazio na frente do forEach e como a call funciona?

    [] é uma matriz.
    Esta matriz não é usada de todo.

    Está sendo colocado na página, porque o uso de um array fornece access a protótipos de array, como .forEach .

    Isso é apenas mais rápido do que digitar Array.prototype.forEach.call(...);

    Em seguida, forEach é uma function que recebe uma function como input …

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

    … e para cada elemento this (onde this é como uma matriz, em que ele tem um length e você pode acessar suas partes como this[1] ) ele irá passar três coisas:

    1. o elemento na matriz
    2. o índice do elemento (terceiro elemento passaria 2 )
    3. uma referência ao array

    Por fim, .call é um protótipo que as funções têm (é uma function que é chamada em outras funções).
    .call terá seu primeiro argumento e replaceá this dentro da function regular com o que você passou call , como o primeiro argumento ( undefined ou null usará window no JS todos os dias, ou será o que você passou, se em “modo estrito” ). O restante dos argumentos será passado para a function original.

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

    Portanto, você está criando uma maneira rápida de chamar a function forEach e está alterando this da matriz vazia para uma lista de todas as tags e, para cada ordem, você está chamando a function forneceu.

    EDITAR

    Conclusão lógica / limpeza

    Abaixo, há um link para um artigo sugerindo que nós descartemos as tentativas de functional programming e fiquemos em looping in-line manual, toda vez que essa solução for hack-ish e inestética.

    Eu diria que enquanto .forEach é menos útil do que suas contrapartes, .map(transformer) , .filter(predicate) , .reduce(combiner, initialValue) , ainda serve para propósitos quando tudo que você realmente quer fazer é modificar o exterior world (não o array), n-times, embora tenha access a arr[i] ou i .

    Então, como lidamos com a disparidade, como Motto é claramente um cara talentoso e conhecedor, e eu gostaria de imaginar que eu sei o que estou fazendo / para onde estou indo (agora e então … … outro vezes é aprendizado de cabeça)?

    A resposta é realmente muito simples, e algo que o tio Bob e Sir Crockford teriam, ambos facepalm, devido à supervisão:

    limpe-o .

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

    Agora, se você está questionando se você precisa fazer isso, a resposta pode muito bem ser não …
    Isso é feito exatamente por … … toda biblioteca (?) Com resources de ordem mais alta hoje em dia.
    Se você estiver usando lodash ou sublinhado ou até mesmo jQuery, todos eles terão uma maneira de pegar um conjunto de elementos e executar uma ação n vezes.
    Se você não está usando tal coisa, então, por favor, escreva o seu próprio.

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

    Atualização para ES6 (ES2015) e além

    Não apenas um método auxiliar auxiliar slice( ) / array( ) / etc facilita a vida das pessoas que querem usar listas como usam arrays (como deveriam), mas também para as pessoas que têm o luxo de operar em ES6 + navegadores de um futuro relativamente próximo, ou de “transpilar” em Babel hoje, você tem resources de linguagem embutidos, o que torna esse tipo de coisa desnecessária.

     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 clean e muito útil.
    Procure os operadores Rest e Spread ; experimentá-los no site do BabelJS; Se a sua pilha de produtos estiver em ordem, use-os em produção com o Babel e uma etapa de construção.


    Não há nenhuma boa razão para não ser capaz de usar a transformação de não-array em array … … apenas não faça uma bagunça no seu código fazendo nada além de colar a mesma linha feia, em todo lugar.

    O método querySelectorAll retorna um NodeList , que é semelhante a um array, mas não é exatamente um array. Portanto, ele não possui um método forEach (que os objects da matriz herdam via Array.prototype ).

    Como um NodeList é semelhante a um array, os methods array trabalharão nele, então usando [].forEach.call você está invocando o método Array.prototype.forEach no contexto do NodeList , como se você tivesse sido capaz de simplesmente faça yourNodeList.forEach(/*...*/) .

    Observe que o literal da matriz vazia é apenas um atalho para a versão expandida, que você provavelmente verá com bastante frequência também:

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

    As outras respostas explicaram esse código muito bem, então vou apenas adicionar uma sugestão.

    Este é um bom exemplo de código que deve ser refatorado para simplicidade e clareza. Em vez de usar [].forEach.call() ou Array.prototype.forEach.call() toda vez que você fizer isso, faça uma function simples:

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

    Agora você pode chamar essa function em vez do código mais complicado e obscuro:

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

    Pode ser melhor escrito usando

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

    O que é document.querySelectorAll('a') retorna um object semelhante a uma matriz, mas não herda do tipo Array . Portanto, chamamos o método Array.prototype object Array.prototype com o contexto como o valor retornado por document.querySelectorAll('a')

    Uma matriz vazia tem uma propriedade forEach em seu protótipo que é um object Function. (O array vazio é apenas uma maneira fácil de obter uma referência à function forEach que todos os objects Array possuem). Objetos de function, por sua vez, possuem uma propriedade call que também é uma function. Quando você invoca a function de call function, ela executa a function com os argumentos fornecidos. O primeiro argumento se torna this na function chamada.

    Você pode encontrar documentação para a function de call aqui . Documentação para forEach está aqui .

    Apenas adicione uma linha:

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

    E voila!

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

    Apreciar 🙂

    Aviso: NodeList é uma class global. Não use essa recomendação se você estiver escrevendo uma biblioteca pública. No entanto, é uma maneira muito conveniente de aumentar a autoeficácia quando você trabalha no site ou no aplicativo node.js.

    Apenas uma solução rápida e suja eu sempre acabo usando. Eu não tocaria em protótipos, como boa prática. Claro, há muitas maneiras de melhorar isso, mas você entende a ideia.

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

    [] sempre retorna um novo array, é equivalente ao new Array() mas é garantido para retornar um array porque Array pode ser sobrescrito pelo usuário, enquanto [] não pode. Portanto, esta é uma maneira segura de obter o protótipo de Array , então, como descrito, call é usada para executar a function no nodelist arraylike (this).

    Chama uma function com um determinado valor e argumentos fornecidos individualmente. mdn

    Norguard explicou WHAT [].forEach.call() faz e James Allardice POR QUE nós fazemos isso: porque querySelectorAll retorna um NodeList que não tem um método forEach …

    A menos que você tenha um navegador moderno como o Chrome 51+, o Firefox 50+, o Opera 38 e o Safari 10.

    Se não, você pode adicionar um 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); } }; }