¿Cómo puedo pasar el valor (no el de referencia) de una variable JS a una función? [duplicar]
-
24-09-2019 - |
Pregunta
Esta pregunta ya tiene una respuesta aquí:
- JavaScript - sencilla práctica ejemplo 44 respuestas
Esta es una versión simplificada de algo que estoy tratando de correr:
for (var i = 0; i < results.length; i++) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
}
pero me estoy encontrando que cada oyente utiliza el valor de results.length (el valor cuando el bucle termina a). ¿Cómo puedo añadir detectores de tal manera que cada uno utiliza el valor de i en el momento añado que, en lugar de la referencia a la i?
Solución
En los navegadores modernos, se pueden utilizar las let
o const
palabras clave para crear una variable con ámbito de bloque:
for (let i = 0; i < results.length; i++) {
let marker = results[i];
google.maps.event.addListener(marker, 'click', () => change_selection(i));
}
En los navegadores más antiguos, es necesario crear un ámbito separado que guarda la variable en su estado actual pasándolo como un parámetro de función:
for (var i = 0; i < results.length; i++) {
(function (i) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
})(i);
}
Al crear una función anónima y llamándola con la variable como primer argumento, estás pasando por valor de la función y la creación de un cierre.
Otros consejos
Además de los cierres, puede utilizar function.bind
:
google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));
pasa el valor de i
en como un argumento de la función cuando se le llama. (null
es para this
, lo que no es necesario en este caso vinculante.)
function.bind
fue introducido por el marco de prototipos y se ha estandarizado en ECMAScript Quinta edición. Hasta navegadores todo el soporte de forma nativa, se puede añadir su propio apoyo function.bind
utilizando cierres:
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))
);
};
};
}
cierres:
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);
}
EDITAR, 2013: Estos ahora se denominan comúnmente como una IIFE
Esta disolución con un cierre. He aquí un artículo sobre los cierres y cómo trabajar con ellos. Salida Ejemplo 5 en la página; ese es el escenario que estamos tratando.
EDIT: Cuatro años después, ese enlace está muerto. La raíz del problema anterior es que los cierres forma el lazo for
(específicamente en marker = results[i]
). A medida que se pasa en marker
addEventListener
, se ve el efecto secundario del cierre: la compartida "medio ambiente" se actualiza con cada iteración del ciclo, antes de que finalmente se "salvó" a través del cierre después de la iteración final. MDN explica esto muy bien.
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));
}
Creo que podemos definir una variable temporal para almacenar el 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);
});
}
No he probado sin embargo.