Como posso percorrer todas as entradas de um array usando JavaScript?
Pensei que era algo parecido com isto:
forEach(instance in theArray)
Onde theArray
é meu array, mas isto parece estar incorreto.
**TL;DR***
for-in
a menos que você o utilize com salvaguardas ou pelo menos esteja ciente do motivo pelo qual ele pode morder você.Array#forEach
(spec
| MDN
) (ou seus parentes some
e tais) (apenas ES5+),for-in
com salvaguardas.
Mas lá's lotes mais para explorar, leia...O JavaScript tem uma semântica poderosa para fazer looping através de arrays e objetos semelhantes a arrays. I'dividiu a resposta em duas partes: Opções para arrays genuínos, e opções para coisas que são apenas semelhantes a arrays, tais como o objeto argumentos
, outros objetos iteráveis (ES2015+), coleções DOM, e assim por diante.
I'irá rapidamente notar que pode utilizar as opções do ES2015 now, mesmo nos motores do ES5, por transpilação* do ES2015 para o ES5. Pesquisar por "ES2015 transpiling" / "ES6 transpiling" para mais...
Ok, deixe's olhar para as nossas opções:
Você tem três opções em ECMAScript 5 ("ES5"), a versão mais amplamente suportada no momento, e mais duas adicionadas em ECMAScript 2015 ("ES2015", "ES6"):
paraCada
e relacionados (ES5+)for-in
correctamente.para cada
e relacionadosEm qualquer ambiente vagamente moderado (portanto, não no IE8) onde você tem acesso às características Array
adicionadas pelo ES5 (diretamente ou utilizando polyfills), você pode utilizar forEach
(spec
| MDN
):
var a = ["a", "b", "c"];
a.forEach(function(entry) {
console.log(entry);
});
paraCada
aceita uma função de callback e, opcionalmente, um valor para utilizar como isto
ao chamar essa função de callback (não utilizada acima). A callback é chamada para cada entrada no array, em ordem, pulando entradas inexistentes em arrays esparsos. Embora eu tenha utilizado apenas um argumento acima, a chamada de retorno é chamada com três: O valor de cada entrada, o índice dessa entrada, e uma referência para o array você're iterating over (no caso da sua função não't já o ter à mão).
A menos que você'esteja suportando navegadores obsoletos como o IE8 (que a NetApps mostra com pouco mais de 4% de participação de mercado a partir desta escrita em setembro 2016), você pode utilizar para cada
em uma página da web de uso geral sem um calço. Se você precisar suportar navegadores obsoletos, o shimming/polyfilling forEach
é facilmente feito (procure por "es5 shim" para várias opções).
O forEach
tem o benefício de que você não'não precisa declarar variáveis de indexação e valor no escopo contendo, pois elas'são fornecidas como argumentos para a função de iteração, e tão bem escopadas apenas para essa iteração.
Se você'está preocupado com o custo de tempo de execução de fazer uma chamada de função para cada entrada de array, don't be; detalhes.
Adicionalmente, `forEach' é o "faz um loop por todos eles" function, mas ES5 definiu vários outros " úteis; trabalhe seu caminho através do array e faça coisas" functions, inclusive:
false
ou algo falso)filtro
(cria um novo array incluindo elementos onde a função de filtro retorna verdadeiro
e omite aqueles onde retorna falso
)map
(cria um novo array a partir dos valores retornados pela callback)reduce
(constrói um valor chamando repetidamente a chamada de retorno, passando em valores anteriores; veja a especificação para os detalhes; útil para somar o conteúdo de um array e muitas outras coisas)Às vezes os velhos métodos são os melhores:
var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
console.log(a[index]);
}
Se o comprimento do array ganhou't mudar durante o loop, e ele's em código sensível ao desempenho (improvável), uma versão um pouco mais complicada agarrando o comprimento na frente pode ser um *tiny** um pouco mais rápido:
var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
console.log(a[index]);
}
E/ou contando para trás:
var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
console.log(a[index]);
}
Mas com os modernos motores JavaScript, é raro que precises de comer aquele último bocado de sumo. No ES2015 e superiores, você pode fazer com que suas variáveis de índice e valor sejam locais para o loop `for':
let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
let value = a[index];
console.log(index, value);
}
//console.log(index); // would cause "ReferenceError: index is not defined"
//console.log(value); // would cause "ReferenceError: value is not defined"
let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
let value = a[index];
console.log(index, value);
}
try {
console.log(index);
} catch (e) {
console.error(e); // "ReferenceError: index is not defined"
}
try {
console.log(value);
} catch (e) {
console.error(e); // "ReferenceError: value is not defined"
}
E quando você faz isso, não apenas "valor" mas também "índice" é recriado para cada iteração de loop, significando que os fechamentos criados no corpo do loop mantêm uma referência ao "índice" (e "valor") criado para essa iteração específica:
let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
divs[index].addEventListener('click', e => {
console.log("Index is: " + index);
});
}
let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
divs[index].addEventListener('click', e => {
console.log("Index is: " + index);
});
}
<div>zero</div>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
Se você fez cinco mergulhos, você'd get "Index é: 0" se você clicou no primeiro e "Index é: 4" se você clicou no último. Isto **não*** funciona se você utilizar var
em vez de let
.
for-in
correctamente.Você'vai fazer com que as pessoas lhe digam para utilizar for-in
, mas isso'não é para que serve for-in
. loops for-in
através das propriedades enumeráveis de um objeto, não os índices de um array. A ordem não é garantida, nem mesmo no ES2015 (ES6). ES2015+ define uma ordem para as propriedades do objeto (via [[OwnPropertyKeys]]
, [[Enumerate]]
, e coisas que as utilizam como Object.getOwnPropertyKeys
), mas não** define que for-in
seguirá essa ordem. (Detalhes em esta outra resposta.)
Os únicos casos de uso real para for-in
em um array são:
It's a [arranjos espaços]14 com lacunas massivas*** nele, ou
// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
if (a.hasOwnProperty(key) && // These checks are
/^0$|^[1-9]\d*$/.test(key) && // explained
key <= 4294967294 // below
) {
console.log(a[key]);
}
}
Note as três verificações:
length
pode ter. (Por exemplo, um array's length cabe em um inteiro de 32 bits sem assinatura) (Props para RobG por apontar em um comentário no post do meu blog que o meu teste anterior foi'tb bem certo.)*
Você faria isso em código inline, é claro. Você'd escreveria uma função utilitária. Talvez:
// Utility function for antiquated environments without `forEach`
var hasOwn = Object.prototype.hasOwnProperty;
var rexNum = /^0$|^[1-9]\d*$/;
function sparseEach(array, callback, thisArg) {
var index;
for (var key in array) {
index = +key;
if (hasOwn.call(a, key) &&
rexNum.test(key) &&
index <= 4294967294
) {
callback.call(thisArg, array[key], index, array);
}
}
}
var a = [];
a[5] = "five";
a[10] = "ten";
a[100000] = "one hundred thousand";
a.b = "bee";
sparseEach(a, function(value, index) {
console.log("Value at " + index + " is " + value);
});
ES2015 adiciona iteradores ao JavaScript. A maneira mais fácil de usar iteradores é a nova declaração `para-of'. É o que parece:
const a = ["a", "b", "c"];
for (const val of a) {
console.log(val);
}
Debaixo das tampas, que recebe um iterador da matriz e faz loops através dele, obtendo os valores a partir dele. Isso não'não tem o problema que utilizar for-in
tem, porque utiliza um iterador definido pelo objeto (o array), e os arrays definem que seus iteradores iteram através de seus entries (não suas propriedades). Ao contrário do `for-in' no ES5, a ordem em que as entradas são visitadas é a ordem numérica de seus índices.
Às vezes, você pode querer usar um iterador explicitamente. Você também pode fazer isso, apesar de ser muito mais difícil do que "para-de-". Parece que é assim:
const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
console.log(entry.value);
}
O iterador é um objeto que corresponde à definição de Iterator na especificação. Seu método 'seguinte' retorna um novo objeto resultado cada vez que você o chama. O objeto resultado tem uma propriedade, done', nos dizendo se ele's está feito, e uma propriedade
value' com o valor para essa iteração. (done
é opcional se for false
, value
é opcional se for undefined
).
O significado de valor
varia dependendo do iterador; as arrays suportam (pelo menos) três funções que retornam os iteradores:
values()
: Esta é a que eu utilizei acima. Ele retorna um iterador onde cada valor
é a entrada de array para aquela iteração ("a"
, "b"
, e "c"
no exemplo anterior).keys()
: Retorna um iterador onde cada valor
é a chave para essa iteração (então para o nosso a' acima, isso seria
"0", então
"1", então
"2"`).entries()
: Retorna um iterador onde cada valor
é um array na forma [chave, valor]
para essa iteração.Além de arrays verdadeiros, há também objetos array-like que têm uma propriedade length
e propriedades com nomes numéricos: instâncias NodeList
, o objeto argumentos
, etc. Como fazemos um loop através do seu conteúdo?
Pelo menos algumas, e possivelmente a maioria ou mesmo todas, das abordagens de array acima frequentemente se aplicam igualmente bem a objetos semelhantes a arrays:
Use forEach
and related (ES5+)
As várias funções em Array.prototype' são "intencionalmente genéricas" e podem ser utilizadas em objetos semelhantes a arrays via [
Function#call][15] ou [
Function#apply']16. (Veja o Caveat para objetos fornecidos pelo host no final desta resposta, mas isso'é um problema raro).
Suponha que você queria utilizar para cada
em uma propriedade Node
's childNodes
. Você'faria isto:
Array.prototype.forEach.call(node.childNodes, function(child) {
// Fazer algo com a "criança
});
Se você'vai fazer isso muitas vezes, você pode querer pegar uma cópia da referência da função em uma variável para reutilização, por exemplo:
// (Isto é tudo, presumivelmente, em alguma função de escopo)
var forEach = Array.prototype.forEach;
// Então mais tarde...
forEach.call(node.childNodes, function(child) {
// Fazer algo com a "criança
});
**Utilize um simples para
loop***
Obviamente, um simples 'para' loop se aplica a objetos semelhantes a arrays.
Use for-in
*correctamente****
O aviso para os objetos fornecidos pelo host no número 1 acima pode se aplicar.
Use for-of
(use um iterador implicitamente) (ES2015+).
O "for-of" utilizará o iterador fornecido pelo objeto (se houver); nós'teremos que ver como isso joga com os vários objetos do tipo arraial, particularmente os fornecidos pelo host. Por exemplo, a especificação para a NodeList
de querySelectorAll
foi atualizada para suportar a iteração. A especificação para a HTMLCollection
de getElementsByTagName
não foi.
5. Use um iterador explicitamente (ES2015+). Veja #4, nós'teremos que ver como os iteradores funcionam.
Outras vezes, você pode querer converter um objeto parecido com um arraial em um verdadeiro array. Fazer isso é surpreendentemente fácil:
**Use o método slice
de arrays**.
Podemos utilizar o método slice
de arrays, que como os outros métodos mencionados acima é "intencionalmente genérico" e assim pode ser utilizado com objetos semelhantes a arrays, como este:
var trueArray = Array.prototype.slice.call(arrayLikeObject);
Então, por exemplo, se quisermos converter uma NodeList
em um array verdadeiro, podemos fazer isso:
var divs = Array.prototype.slice.call(document.querySelectorAll("div"));
Veja o Caveat para objetos fornecidos pelo host* abaixo. Em particular, note que isso irá falhar no IE8 e anteriores, que don'não permite que você utilize objetos fornecidos pelo host como aquilo
assim.
Use sintaxe de propagação (...
)
Também é possível usar o ES2015's sintaxe de propagação com motores JavaScript que suportam esta funcionalidade:
var trueArray = [...iterableObject];
Então, por exemplo, se quisermos converter uma NodeList
em um array verdadeiro, com a sintaxe de spread isso se torna bastante sucinto:
var divs = [...document.querySelectorAll("div")];
Use Array.from
(spec) | (MDN)
Array.from' (ES2015+, mas facilmente polifilar) cria um array a partir de um objeto tipo arrays, opcionalmente passando as entradas através de uma função de mapeamento primeiro. Então:
var divs = Array.from(document.querySelectorAll("div"));
Ou se você quisesse obter um array dos nomes das tags dos elementos com uma determinada classe, você'd usaria a função de mapeamento:
// Função de seta (ES2015):
var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
// função padrão (já que `Array.from' pode ser shimmed):
var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
retorna elemento.tagName;
});
Se você utiliza as funções Array.prototype' com *objetos do tipo arrays (listas DOM e outras coisas fornecidas pelo navegador ao invés do motor JavaScript), você precisa ter certeza de testar em seus ambientes alvo para ter certeza de que o objeto fornecido pelo host se comporta corretamente. **A maioria comporta-se corretamente** (agora), mas é's importante testar. A razão é que a maioria dos métodos
Array.prototype' que você'é provável que você queira utilizar, confie no objeto fornecido pelo host, dando uma resposta honesta à operação abstrata [[HasProperty]]
. A partir desta escrita, os navegadores fazem um bom trabalho, mas a especificação 5.1 permitiu a possibilidade de um objeto fornecido pelo host poder não ser honesto. It's em §8.6.2, vários parágrafos abaixo da grande tabela perto do início daquela seção), onde diz:
Objetos host podem implementar esses métodos internos de qualquer maneira, a menos que especificado de outra forma; por exemplo, uma possibilidade é que
[[Get]]
e[[Put]]
para um objeto host em particular busque e armazene valores de propriedades, mas[[HasProperty]]
sempre gera falso. (Eu não consegui'não encontrei a verborreia equivalente na especificação ES2015, mas é claro que's ainda é o caso). Mais uma vez, a partir desta escrita, os objetos comuns do tipo arrays de host fornecidos em browsers modernos [instânciasNodeList
, por exemplo] do manipulam[[HasProperty]]
corretamente, mas é importante testar's).
**Nota***: Esta resposta está irremediavelmente desactualizada. Para uma abordagem mais moderna, veja os métodos disponíveis em um array. Métodos de interesse podem ser:
*paraCada
A forma padrão de iterar um array em JavaScript é um vanilla for
-loop:
var length = arr.length,
element = null;
for (var i = 0; i < length; i++) {
element = arr[i];
// Do something with element
}
Note, no entanto, que esta abordagem só é boa se você tiver uma matriz densa, e cada índice é ocupado por um elemento. Se o array é esparso, então você pode ter problemas de performance com esta abordagem, já que você irá iterar sobre muitos índices que não existem realmente no array. Neste caso, um for ... in'-loop pode ser uma idéia melhor. **Porém***, você deve utilizar as salvaguardas apropriadas para garantir que somente as propriedades desejadas do array (ou seja, os elementos do array) sejam atuadas, já que o
for..in-loop também será enumerado em navegadores legados, ou se as propriedades adicionais forem definidas como
enumeráveis'.
Em ECMAScript 5 haverá um método forEach no protótipo de array, mas ele não é suportado em navegadores legados. Então para poder usá-lo consistentemente você deve ter um ambiente que o suporte (por exemplo, Node.js para JavaScript do lado do servidor), ou usar um "Polyfill". O Polyfill para esta funcionalidade é, no entanto, trivial e como torna o código mais fácil de ler, é um bom polyfill para incluir.
Se você quiser fazer um loop sobre um array, utilize o loop padrão de três partes `para'.
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
}
Você pode obter algumas otimizações de desempenho caching myArray.length
ou iterating over it backwards.