Avatar ·

Como fazer closures em JavaScript lembrarem o valor da variável de sua iteração em um loop?

Olá a todos! Sou novo em JavaScript e me deparei com um problema que não entendo. Estou escrevendo um código simples: quero adicionar handlers de clique a vários botões, para que cada um exiba seu número. Aqui está meu código: ```javascript const buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length; i++) { buttons[i].addEventListener('click', function() { console.log('Botão número ' + i); }); } ``` Mas quando clico em qualquer botão, o console sempre exibe "Botão número 5" (o último valor de i), independentemente de qual botão eu pressionei. Li que isso está relacionado a closures, mas não entendo como corrigir. Por que isso acontece? Como fazer cada botão lembrar seu número? Explique, por favor, em palavras simples com exemplos. Desde já, obrigado! <<>>

Por que o problema ocorre?

O problema está relacionado a como closures e o escopo da variável declarada com var funcionam. A palavra-chave var tem escopo de função, não de bloco. No loop for, a variável i é a mesma para todas as iterações. Quando o evento de clique é disparado, o loop já terminou e i tem o valor final (o número total de botões). Como a função callback captura a referência à variável i (não seu valor no momento da criação), todas as funções veem o mesmo valor final.

Como corrigir?

Existem várias soluções. A mais comum é usar let em vez de var, pois let tem escopo de bloco e cria uma nova variável para cada iteração:

for (let i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener('click', function() {
        console.log('Botão número ' + i);
    });
}

Outra abordagem é usar uma IIFE (Immediately Invoked Function Expression) para capturar o valor de i em cada iteração:

for (var i = 0; i < buttons.length; i++) {
    (function(index) {
        buttons[index].addEventListener('click', function() {
            console.log('Botão número ' + index);
        });
    })(i);
}

Ou usar forEach que já fornece o índice como parâmetro:

buttons.forEach(function(button, index) {
    button.addEventListener('click', function() {
        console.log('Botão número ' + index);
    });
});