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?


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.


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 ()

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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.