Como faço para passar o valor (não a referência) de uma variável JS para uma função? [duplicado
-
24-09-2019 - |
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?
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.