Com a chegada do ECMAScript 6 (2015), ganhamos o laço de iteração for…of. A ideia é que o utilizemos para iterar objetos iteráveis, ou seja, que definem em sua estrutura – através de uma função geradora, na propriedade [Symbol.iterator] – como ele deve ser percorrido. Por padrão, alguns objetos no JavaScript já são iteráveis, como é o caso do Array, Map, Set e o próprio objeto String.
Seu uso é bastante simples. Imagine que temos um array de números naturais e queremos exibir seus valores no console. Com o laço de repetição for…of, basta fazermos isso:
const numeros = [1,2,3,4,5]; for(let numero of numeros) { console.log(numero); } // resultado: 1, 2, 3, 4, 5
Bem tranquilo, certo? Mas o que acontece é que muita gente confunde esse novo tipo de laço com o já antigo laço for…in. Será que eles são a mesma coisa? Vamos colocar a prova. Vou fazer a mesma iteração do exemplo anterior, só mudando o of para in.
const numeros = [1,2,3,4,5]; for(let numero in numeros) { console.log(numero); } // resultado: 0, 1, 2, 3, 4
Espera aí, o resultado foi diferente! Mas por quê? Vamos entender o que está acontecendo. Se consultarmos a definição do laço no site do MDN, veremos que o laço for…in “interage sobre propriedades enumeradas de um objeto, na ordem original de inserção”. Na prática, isso significa que o laço enxerga as propriedades e não os seus valores.
Isso fica mais fácil de entender se tentarmos iterar esse objeto Casa que possui 3 propriedades: área, altura, andares. Repare no que acontece:
const Casa = { area: 1000, altura: 7, andares: 2 } for(let prop in Casa) { console.log(prop); } // Resultado // area // altura // andares
Percebeu a diferença? O laço for…of itera os valores das propriedades (neste caso), enquanto o laço for…in itera as propriedades. Mas não pense que basta alterar o in por of que este exemplo funcionará, porque é isso o que acontece:
TypeError: Casa is not iterable
O que este erro quer dizer? Assim como já comentei no início do artigo, o laço for…of procura pela propriedade [Symbol.iterator] do objeto para conseguir iterá-lo. O que este erro está nos dizendo é que ele não foi definido nesta estrutura. Para resolver este problema, podemos fazer desta maneira:
const Casa = { area: 1000, altura: 7, andares: 2, [Symbol.iterator]: function* (){ yield this.area; yield this.altura; yield this.andares; } }
Agora, sim, temos o resultado esperado:
for(let prop of Casa) { console.log(prop); } // Resultado // 1000, 7, 2
Conclusão
Os laços for…of e for…in apesar de serem bem parecidos, fazem coisas distintas. Enquanto o primeiro procura dentro da estrutura pela propriedade [Symbol.iterator] que define como ela deve ser iterada, o segundo itera pelas propriedades enumeradas do objeto. Os objetos iteráveis por definição no JavaScript já descrevem a propriedade para que ela exiba os seus valores (ex: Map, Set, Array), mas em qualquer outro tipo de estrutura essa decisão fica por conta do desenvolvedor.
Se você gostou deste artigo ou ainda está com dúvidas, te convido a visitar este material. Lá você encontrará o meu livro Entendendo o ECMAScript 6 – Entre de cabeça no futuro do JavaScript publicado pela Casa do Código e o curso completo Entendendo o ECMAScript 6 na Udemy, publicado pela Code Prestige.