Desafio JavaScript
Loop com closures: var vs let
Enunciado
Crie uma função que retorna um array de funções. Cada função, ao ser chamada, deve imprimir seu índice (0, 1, 2). Use let no loop para obter o comportamento correto. Em seguida, mostre como var causaria um problema e como corrigi-lo com uma IIFE (Immediately Invoked Function Expression).
Requisitos
- A função
criarFuncoesLetdeve retornar um array de 3 funções que imprimem 0, 1 e 2 respectivamente. - A função
criarFuncoesVardeve usarvare demonstrar o problema (todas imprimem 3). - A função
criarFuncoesVarCorrigidodeve usarvarcom IIFE para corrigir o problema. - O programa deve chamar cada função e imprimir os valores na ordem correta.
Código inicial
function criarFuncoesLet() {
// seu código aqui
}
function criarFuncoesVar() {
// seu código aqui
}
function criarFuncoesVarCorrigido() {
// seu código aqui
}
// Testes
console.log('Com let:');
criarFuncoesLet().forEach(fn => fn());
console.log('Com var (problema):');
criarFuncoesVar().forEach(fn => fn());
console.log('Com var corrigido:');
criarFuncoesVarCorrigido().forEach(fn => fn());
Saída esperada
Com let:
0
1
2
Com var (problema):
3
3
3
Com var corrigido:
0
1
2
Ver dica
Com let, cada iteração cria um novo escopo. Com var, o escopo é único. A IIFE cria um novo escopo a cada iteração.
Mostrar solução
function criarFuncoesLet() {
const funcoes = [];
for (let i = 0; i < 3; i++) {
funcoes.push(() => console.log(i)); // cada iteração tem seu próprio i
}
return funcoes;
}
function criarFuncoesVar() {
const funcoes = [];
for (var i = 0; i < 3; i++) {
funcoes.push(() => console.log(i)); // todas compartilham o mesmo i
}
return funcoes;
}
function criarFuncoesVarCorrigido() {
const funcoes = [];
for (var i = 0; i < 3; i++) {
(function (indice) {
funcoes.push(() => console.log(indice)); // captura o valor atual
})(i);
}
return funcoes;
}
// Testes
console.log('Com let:');
criarFuncoesLet().forEach(fn => fn());
console.log('Com var (problema):');
criarFuncoesVar().forEach(fn => fn());
console.log('Com var corrigido:');
criarFuncoesVarCorrigido().forEach(fn => fn());
Passo a passo
criarFuncoesLetusalet ino loop. A cada iteração, um novo escopo de bloco é criado, então cada closure captura umidiferente.criarFuncoesVarusavar i. Comovartem escopo de função, todas as closures compartilham o mesmoi, que após o loop vale 3.criarFuncoesVarCorrigidousa uma IIFE que recebeicomo parâmetroindice, criando um novo escopo a cada chamada.- As funções são armazenadas em um array e retornadas.
- Ao chamar cada função, vemos os valores corretos.
Por que funciona
let cria um novo binding a cada iteração do loop, então cada closure captura seu próprio i. Com var, o binding é único e compartilhado, resultando em 3 para todas. A IIFE simula o escopo de bloco criando uma nova função a cada iteração.
Erros comuns
- Usar
vare esperar valores diferentes: todas as funções imprimem o valor final dei. - Esquecer de invocar a IIFE:
(function(indice) { ... })sem(i)não executa. - Usar
letfora do loop: perde o escopo de bloco por iteração.
Outra forma de resolver
Poderia usar forEach ou map, que criam escopos automaticamente:
function criarFuncoesForEach() {
const funcoes = [];
[0, 1, 2].forEach(i => {
funcoes.push(() => console.log(i));
});
return funcoes;
}
Essa abordagem é mais idiomática e evita a necessidade de IIFE.
Saída esperada
Com let:
0
1
2
Com var (problema):
3
3
3
Com var corrigido:
0
1
2