Filtrar ou mapear listas de nós em ES6

87

Qual é a maneira mais eficiente de filtrar ou mapear uma lista de nós no ES6?

Com base em minhas leituras, eu usaria uma das seguintes opções:

[...nodelist].filter

ou

Array.from(nodelist).filter

Qual desses você recomendaria? E existem maneiras melhores, por exemplo, sem envolver matrizes?

Christophe
fonte
2
Basicamente, os dois métodos fazem a mesma coisa. Se você estiver usando babel, [...coll]simplesmente solicitará Array.from(coll)qualquer coisa que não seja um Array.
Leonid Beschastny
FWIW, a ...sintaxe pode não ser suportada por IDEs mais antigos, embora Array.from()seja apenas um método regular.
Marat Tanalin

Respostas:

126
  • [...nodelist] fará um array de fora de um objeto se o objeto for iterável.
  • Array.from(nodelist)fará uma matriz de um objeto se o objeto for iterável ou se o objeto for semelhante a uma matriz (tem .lengthe props numéricos)

Seus dois exemplos serão idênticos se NodeList.prototype[Symbol.iterator]existirem, porque ambos os casos cobrem iteráveis. Se o seu ambiente não foi configurado de forma NodeListiterável, o primeiro exemplo falhará e o segundo terá êxito. Babelatualmente não lida com este caso adequadamente .

Portanto, se o seu NodeListfor iterável, você realmente decide qual será o seu uso. Eu provavelmente escolheria caso a caso. Um benefício do Array.fromé que leva um segundo argumento de uma função de mapeamento, enquanto o primeiro [...iterable].map(item => item)teria que criar um array temporário, Array.from(iterable, item => item)não. Se você não estiver mapeando a lista, não importa.

loganfsmyth
fonte
17

TL; DR;

Array.prototype.slice.call(nodelist).filter

O método slice () retorna uma matriz. Essa matriz retornada é uma cópia superficial da coleção (NodeList). Portanto, funciona mais rápido do que Array.from (). Portanto, funciona tão rápido quanto Array.from ()

Elementos da coleção original são copiados para a matriz retornada da seguinte maneira:

  • Para referências de objeto (e não o objeto real), a fatia copia as referências de objeto para a nova matriz. Tanto a matriz original quanto a nova se referem ao mesmo objeto. Se um objeto referenciado for alterado, as alterações serão visíveis para os arrays novos e originais.
  • Para strings, números e booleanos (não String, Number e objetos Boolean), o slice copia os valores para o novo array. Mudanças na string, número ou booleano em um array não afetam o outro array.

Breve explicação sobre os argumentos

Array.prototype.slice (beginIndex, endIndex)

  • usa argumentos opcionais beginIndex e endIndex. Se eles não forem fornecidos, as fatias usam beginIndex == 0, portanto, extrai todos os itens da coleção

Array.prototype.slice.call (namespace, beginIndex, endIndex)

  • leva um objeto como o primeiro argumento. Se usarmos uma coleção como um objeto, significa literalmente que chamamos o método slice diretamente desse objeto namespace.slice ()
Serge Seletskyy
fonte
2
Obrigado por este trecho de código, que pode fornecer alguma ajuda limitada e imediata. Uma explicação adequada melhoraria muito seu valor a longo prazo, mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras questões semelhantes. Edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
Maximilian Peters,
Estou me perguntando se isso tem suporte para o IE, já Array.fromque não. É hora de encontrar uma máquina IE. Agora estou realmente confuso porque consegui usar Array.from no IE10 e no IE11: \. Este método funciona no IE10 + 11, mas Array.from não me deixa funcionar quando toda a documentação diz o contrário.
CTS_AE
Array.fromnão funciona para mim no IE11 O objeto não suporta propriedade ou método 'de'
Fus Ro Dah
Obrigado, isso funcionou para mim em uma implementação antiga de JavaScript
Vic Seedoubleyew
1
Array.fromtambém retorna uma cópia superficial. Portanto, não vejo como você conclui que funciona mais rápido do que Array#slice.
Robert
9

Encontrei uma referência que usa mapdiretamente na NodeList por

Array.prototype.map.call(nodelist, fn)

Não testei, mas parece plausível que seja mais rápido porque deve acessar o NodeList diretamente.

Goweon
fonte
2

Que tal agora:

// Be evil. Extend the prototype.
if (window.NodeList && !NodeList.prototype.filter) {
  NodeList.prototype.filter = Array.prototype.filter;
}

// Use it like you'd expect:
const noClasses = document
  .querySelectorAll('div')
  .filter(div => div.classList.length === 0)

É a mesma abordagem mencionada nos documentos MDN para NodeList.forEach (em 'Polyfill'), funciona para IE11 , Edge, Chrome e FF.

panéter
fonte