2009-09-12 14 views
6

Consideremos el siguiente código Javascript:Javascript cierre evaluación inmediata

var a = []; 

var f = function() { 

    for (var i = 0; i < 3; i++) { 
     a.push(function(){alert(i)}); 
    } 
    for (var j = 0; j < 3; j++) { 
     a[j](); 
    } 
}; 

Las alertas imprimen '3' las tres veces. Quiero un comportamiento diferente: en cada iteración del ciclo, se genera una función que imprime el valor actual de i. Es decir. 3 funciones que imprimen diferentes índices.

¿Alguna idea?

+0

Solo para agregar esto es porque Javascript no tiene el concepto de ámbito de bloque solo ámbito de función, esto me arrojó también ... http://www.mattfreeman.co.uk/2010/03/closures-scope-in- javascript-vs-c/ –

Respuesta

7

Crear una función anónima que acepta i como parámetro y devuelve esa función determinada:

for (var i = 0; i < 3; i++) { 
    a.push((function(i) { 
     return function() { 
      alert(i); 
     } 
    })(i)); 
} 

for (var j = 0; j < 3; j++) { 
    a[j](); 
} 

O hacer algo similar: crear una función anónima que acepta i como un parámetro para agregar la función de la matriz:

for (var i = 0; i < 3; i++) { 
    (function(i) { 
     a.push(function() { 
      alert(i); 
     }); 
    })(i); 
} 

for (var j = 0; j < 3; j++) { 
    a[j](); 
} 
+1

No es necesario, pero creo que se ve más limpio, y describe mejor sus intenciones para ajustar sus funciones "ejecutadas inmediatamente" en '()' -> '(función (i) {...}) (i); ' – gnarf

+0

@gnarf, yo mismo estaba debatiéndolo. Supongo que hace que la intención sea más clara. Voy a editar eso en. – strager

+0

esto parece desviar el problema original al ofrecer una solución alternativa que no es susceptible a los mismos defectos subyacentes ... Lo que estás haciendo aquí es presionar valores en una matriz. El póster original está presionando funciones, que, supongo, se ejecutarán en algún momento posterior ... – Funka

1
var iterate = (function() { 
    var i, j = []; 
    for (i = 0; i < 3; i += 1) { 
     j.push(i); 
     alert(j[j.length - 1]); 
    } 
}()); 

no es necesario el cierre a la mera salida de un valor. Sin embargo, su código debe estar contenido en una función para la contención orientada a objetos. Las funciones no tienen que ser llamadas para ser ejecutadas.

+0

"No es necesario llamar a las funciones para su ejecución". ¿Qué? Esa afirmación no es muy clara, y tal como está suena mal. Aclarar, por favor? – strager

+0

Para ejecutar una función, una de tres cosas debe suceder. 1) La función debe llamarse por nombre por otra cosa que se está ejecutando. 2) La función se puede insertar en un método, en cuyo caso la función puede ser anónima y aún así ejecutarse. Me opongo rotundamente al uso de funciones sin darles un nombre. 3) Las funciones pueden ejecutarse completamente por sí mismas, ya que se interpretan si terminan con un paréntesis después de su corchete de cierre, como}(). Eso se llama evocación inmediata. –

+0

No estoy de acuerdo. Una función es un tipo de valor, como '42' o' 'hello world'', en Javascript. Si se asigna o no a una variable o se usa directamente no significa nada especial. Para un ejemplo de este comportamiento, ejecute: '(function (i) {var func = arguments.callee; if (! I) return; console.log ('x'); window.setTimeout (function() {func (i - 1);}, 1000);}) (4); ' – strager

-2

función (i) {alert (i)

+2

Es muy probable que 'i' no esté definido. – strager

+0

+1 por concisión. Esto funcionará si a.push (función (i) {alerta (i)}); se usa en lugar de a.push (function() {alert (i)}); –

6

Sólo otro enfoque, utilizando currying:

var a = []; 
var f = function() { 
    for (var i = 0; i < 3; i++) { 
     a.push((function(a){alert(a);}).curry(i)); 
    } 
    for (var j = 0; j < 3; j++) { 
     a[j](); 
    } 
}; 

// curry implementation 
Function.prototype.curry = function() { 
    var fn = this, args = Array.prototype.slice.call(arguments); 
    return function() { 
    return fn.apply(this, args.concat(
     Array.prototype.slice.call(arguments))); 
    }; 
}; 

Comprobar el fragmento anterior here corriendo.

+1

Buen uso del curry, aunque ahora tengo hambre ... – gnarf

+0

Wow. Eso es genial. +1. – strager

0

Se puede poner el cuerpo de su bucle en una función anónima:

var a = []; 

for(var i = 0; i < 3; i++) (function(i) { 
    a.push(function() { alert(i); }); 
})(i) 

for(var j = 0; j < 3; j++) { 
    a[j](); 
} 

Mediante la creación de esa función y pasar el valor del bucle de la "i" como el argumento, estamos creando una nueva variable "i" dentro del cuerpo del bucle que esencialmente oculta la "i" exterior. El cierre que presione en la matriz ahora ve la nueva variable, cuyo valor se establece cuando se llama a la función de función externa en el primer ciclo. Esto podría ser más claro si se utiliza un nombre diferente cuando creamos la nueva variable ... Esto hace lo mismo:

var a = []; 

for(var i = 0; i < 3; i++) (function(iNew) { 
    a.push(function() { alert(iNew); }); 
})(i) 

for(var j = 0; j < 3; j++) { 
    a[j](); 
} 

El valor de "Inew" se asigna 0, 1, 2 y luego porque la función es llamado inmediatamente por el ciclo.