Pergunta

Esta pergunta já tem uma resposta aqui:

Aqui está uma versão simplificada de algo que estou tentando correr:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
        change_selection(i);
    }); 
}

Mas estou descobrindo que todo ouvinte usa o valor dos resultados.Length (o valor quando o loop for termina). Como posso adicionar ouvintes de modo que cada um use o valor de i no momento em que o adiciono, em vez da referência a i?

Foi útil?

Solução

Nos navegadores modernos, você pode usar o let ou const Palavras-chave para criar uma variável escopo de blocos:

for (let i = 0; i < results.length; i++) {
  let marker = results[i];
  google.maps.event.addListener(marker, 'click', () => change_selection(i));
}

Nos navegadores mais antigos, você precisa criar um escopo separado que salve a variável em seu estado atual, passando -o como um parâmetro de função:

for (var i = 0; i < results.length; i++) {
  (function (i) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
      change_selection(i);
    }); 
  })(i);
}

Ao criar uma função anônima e chamá-la com a variável como o primeiro argumento, você está passando por valor para a função e criando um fechamento.

Outras dicas

Bem como os fechamentos, você pode usar function.bind:

google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));

passa o valor de i como um argumento para a função quando chamado. (null é para ligação this, que você não precisa neste caso.)

function.bind foi introduzido pela estrutura do protótipo e foi padronizado na quinta edição do Ecmascript. Até que todos os navegadores suportem nativamente, você pode adicionar o seu próprio function.bind Suporte usando fechamentos:

if (!('bind' in Function.prototype)) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}

fechamento:

for (var i = 0, l= results.length; i < l; i++) {
    marker = results[i];
    (function(index){
        google.maps.event.addListener(marker, 'click', function() { 
            change_selection(index);
        }); 
    })(i);
}

Edit, 2013:Estes agora são comumente referidos como um Iife

Você está terminando com um fechamento. Aqui está um artigo sobre fechamentos e como trabalhar com eles. Confira o Exemplo 5 na página; Esse é o cenário com o qual você está lidando.

EDIT: Quatro anos depois, esse link está morto. A raiz da questão acima é que o for O loop forma o fechamento (especificamente em marker = results[i]). Como marker é passado addEventListener, você vê o efeito colateral do fechamento: o "ambiente" compartilhado é atualizado a cada iteração do loop, antes de finalmente ser "salvo" através do fechamento após a iteração final. MDN explica isso muito bem.

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', (function(i) {
        return function(){
            change_selection(i);
        }
    })(i)); 
}

Acho que podemos definir uma variável temporária para armazenar o valor de i.

for (var i = 0; i < results.length; i++) {
 var marker = results[i];
 var j = i;
 google.maps.event.addListener(marker, 'click', function() { 
   change_selection(j);
 }); 
}

Eu não testei.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top