for...of e for...in
Nesta aula, exploramos os laços `for...of` e `for...in` em JavaScript, entendendo como iterar sobre arrays e objetos, suas diferenças cruciais e pegadinhas comuns. Ao final, você será capaz de escolher o laço adequado para cada situação e evitar erros sutis.
Os laços for...of e for...in são duas construções poderosas em JavaScript para percorrer coleções de dados. Embora pareçam semelhantes, eles têm propósitos e comportamentos distintos que podem gerar confusão. Nesta aula, vamos mergulhar em cada um deles, entender quando usar cada um e, principalmente, quais armadilhas evitar.
Iterando arrays e objetos
O laço for...of foi introduzido no ES6 e é projetado para iterar sobre objetos iteráveis, como arrays, strings, Map, Set, etc. Ele percorre os valores dos elementos, ignorando propriedades não numéricas ou herdadas. Por outro lado, for...in itera sobre as propriedades enumeráveis de um objeto, incluindo as herdadas do protótipo. É importante notar que for...in não é recomendado para arrays, pois pode trazer surpresas.
Vamos ver exemplos práticos:
// Exemplo com for...of em array
const frutas = ['maçã', 'banana', 'laranja'];
for (const fruta of frutas) {
console.log(fruta); // 'maçã', 'banana', 'laranja'
}
// Exemplo com for...in em array
for (const indice in frutas) {
console.log(indice); // '0', '1', '2' (strings!)
console.log(frutas[indice]); // 'maçã', 'banana', 'laranja'
}
// Exemplo com for...in em objeto
const pessoa = { nome: 'João', idade: 30 };
for (const chave in pessoa) {
console.log(chave + ': ' + pessoa[chave]);
}
// nome: João
// idade: 30
// for...of em objeto (não funciona diretamente)
// for (const valor of pessoa) { // TypeError: pessoa is not iterable
// console.log(valor);
// }
No código acima, vemos que for...of itera sobre os valores do array, enquanto for...in itera sobre as chaves (índices) como strings. Para objetos, for...in funciona bem para acessar propriedades próprias e enumeráveis. Já for...of requer que o objeto seja iterável, o que objetos comuns não são, a menos que implementem o protocolo de iteração.
Diferença crucial entre eles
A diferença fundamental é que for...in itera sobre as chaves (propriedades) de um objeto, enquanto for...of itera sobre os valores de um iterável. Isso tem implicações importantes:
- Tipo de dado retornado:
for...inretorna strings (nomes das propriedades);for...ofretorna os próprios valores. - Escopo de uso:
for...offunciona apenas com iteráveis (arrays, strings, Map, Set, etc.), enquantofor...infunciona com qualquer objeto. - Herança:
for...inpercorre também propriedades herdadas do protótipo, a menos que sejam não enumeráveis.for...ofnão se preocupa com herança. - Performance:
for...oftende a ser mais rápido em arrays porque não precisa lidar com a cadeia de protótipos.
Veja um exemplo que ilustra a herança:
function Animal() {}
Animal.prototype.som = 'grr';
const cachorro = new Animal();
cachorro.nome = 'Rex';
// for...in inclui 'som' (herdado) e 'nome'
for (const prop in cachorro) {
if (cachorro.hasOwnProperty(prop)) {
console.log('Própria: ' + prop); // apenas 'nome'
} else {
console.log('Herdada: ' + prop); // 'som'
}
}
// for...of não se aplica a objetos comuns
Para evitar propriedades herdadas, use o método hasOwnProperty dentro do loop for...in.
Pegadinhas
Algumas armadilhas comuns ao usar esses laços:
- Usar
for...inem arrays: Como vimos,for...initera sobre índices como strings e também pode incluir propriedades adicionadas ao array (ex.:array.novaProp = 'valor'). Além disso, a ordem de iteração não é garantida, embora na prática seja numérica para índices. - Esquecer que
for...ininclui propriedades herdadas: Se você estender o protótipo deArrayouObject, isso afetará o loop. Sempre usehasOwnPropertyou prefirafor...ofpara arrays. - Tentar usar
for...ofem objetos comuns: Isso gera um erro. Para iterar sobre objetos, useObject.keys(),Object.values()ouObject.entries()combinados comfor...of. - Confundir a variável de iteração: Em
for...in, a variável recebe o nome da chave (string). Emfor...of, recebe o valor. Misturar isso pode causar bugs.
Exemplo de pegadinha com for...in em array:
const arr = [10, 20, 30];
arr.novaProp = 'Olá';
for (const i in arr) {
console.log(i + ': ' + arr[i]);
}
// 0: 10
// 1: 20
// 2: 30
// novaProp: Olá
Já com for...of, a propriedade extra é ignorada:
for (const valor of arr) {
console.log(valor); // 10, 20, 30 (apenas)
}
Boas práticas
Para evitar problemas, siga estas recomendações:
- Use
for...ofpara arrays, strings, Map, Set e outros iteráveis. - Use
for...inapenas para objetos simples (não arrays) quando precisar das chaves. - Sempre filtre propriedades herdadas com
hasOwnPropertydentro defor...in. - Considere usar métodos como
forEach,map,filterpara arrays, que são mais expressivos e menos propensos a erros.
Referências
- MDN: for...of
- MDN: for...in
- MDN: Loops e iteração
- JavaScript.info: for...in vs for...of
- W3Schools: JavaScript For In
- W3Schools: JavaScript For Of
Exercícios
-
Dado o array
const nums = [1, 2, 3];, usefor...ofpara imprimir cada número ao quadrado.✓ Resposta:const nums = [1, 2, 3]; for (const num of nums) { console.log(num * num); } -
Use
for...inpara listar as propriedades do objetoconst carro = { marca: 'Fiat', modelo: 'Uno' };, incluindo as herdadas. Depois, modifique para listar apenas as propriedades próprias.✓ Resposta:const carro = { marca: 'Fiat', modelo: 'Uno' }; // Todas as propriedades (incluindo herdadas) for (const prop in carro) { console.log(prop + ': ' + carro[prop]); } // Apenas próprias for (const prop in carro) { if (carro.hasOwnProperty(prop)) { console.log(prop + ': ' + carro[prop]); } } -
Explique por que o código a seguir pode gerar resultados inesperados:
const arr = [1, 2, 3]; Array.prototype.extra = 'X'; for (const i in arr) { console.log(i); }✓ Resposta:O
for...initera sobre todas as propriedades enumeráveis, incluindo a propriedadeextraadicionada ao protótipo de Array. Portanto, além dos índices '0', '1', '2', também será impresso 'extra'. Para evitar isso, usefor...ofou verifiquehasOwnProperty. -
Dado o objeto
const pessoa = { nome: 'Ana', idade: 25 };, usefor...ofcomObject.entries()para imprimir chave e valor.✓ Resposta:const pessoa = { nome: 'Ana', idade: 25 }; for (const [chave, valor] of Object.entries(pessoa)) { console.log(chave + ': ' + valor); } -
Qual é a saída do código abaixo? Justifique.
const str = 'abc'; for (const letra of str) { console.log(letra.toUpperCase()); }✓ Resposta:A saída será 'A', 'B', 'C' (cada um em uma linha). O laço
for...ofitera sobre os valores da string, que são cada caractere. O métodotoUpperCase()converte para maiúscula.