2009-09-12 19 views
6

Si consideri il seguente codice JavaScript:javascript chiusura immediata valutazione

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](); 
    } 
}; 

Gli avvisi stampano '3' tutte e tre le volte. Voglio un comportamento diverso - in ogni iterazione del ciclo genera una funzione che stampa il valore corrente di i. Cioè 3 funzioni che stampano diversi indici.

Qualche idea?

+0

Solo per aggiungere questo è dovuto al fatto che Javascript non ha il concetto di scope scope scope scope, anche questo mi ha gettato ... http://www.mattfreeman.co.uk/2010/03/closures-scope-in- javascript-vs-c/ –

risposta

7

Creare una funzione anonima che accetta i come parametro e restituisce che certa funzione:

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 fare qualcosa di simile: creare una funzione anonima che accetta i come parametro per aggiungere la funzione alla matrice:

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

Non è necessario, ma penso che sia più pulito e descrive meglio le tue intenzioni per includere le funzioni "immediatamente eseguite" in '()' -> '(function (i) {...}) (i); ' – gnarf

+0

@gnarf, stavo discutendo anch'io. Immagino che rende l'intento più chiaro. Lo modificherò in. – strager

+0

questo sembra aggirare il problema originale offrendo una soluzione alternativa che non è suscettibile agli stessi difetti di fondo ... Quello che stai facendo qui è spingere i valori su un array. Il poster originale sta spingendo le funzioni, che, presumiamo, devono essere eseguite in un secondo momento ... – Funka

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

non è necessario la chiusura a solo uscita un valore. Il tuo codice dovrebbe, tuttavia, essere contenuto in una funzione per il contenimento orientato agli oggetti. Le funzioni non devono essere chiamate per essere eseguite.

+0

"Le funzioni non devono essere chiamate per essere eseguite." Che cosa? Questa affermazione non è molto chiara, e poiché sembra sbagliata. Chiarire, per favore? – strager

+0

Per eseguire una funzione è necessario che si verifichi una delle tre cose. 1) La funzione deve essere chiamata per nome da qualcos'altro che sta eseguendo. 2) La funzione può essere inserita in un metodo, nel qual caso la funzione può essere anonima e comunque eseguita. Mi oppongo fermamente all'utilizzo di funzioni senza dare loro un nome. 3) Le funzioni possono essere eseguite da sole come sono interpretate se sono terminate con una parentesi dopo la parentesi di chiusura, come ad esempio}(). Questa è chiamata evocazione immediata. –

+0

Non sono d'accordo. Una funzione è un tipo di valore, proprio come '42' o' 'hello world'', in Javascript. Indipendentemente dal fatto che sia assegnato o meno a una variabile o utilizzato direttamente non significa nulla di speciale. Per un esempio di questo comportamento, eseguire: '(function (i) {var func = arguments.callee; if (! I) return; console.log ('x'); window.setTimeout (function() {func (i - 1);}, 1000);}) (4); ' – strager

-2

funzione (i) {alert (i)

+2

Più che probabile, 'i' non sarà definito. – strager

+0

+1 per la precisione. Funzionerà se a.push (function (i) {alert (i)}); viene utilizzato al posto di a.push (function() {alert (i)}); –

6

Solo un altro approccio, utilizzando 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))); 
    }; 
}; 

Controllare il frammento di codice in esecuzione here.

+1

Bel uso di curry - anche se ora ho fame .... – gnarf

+0

Wow. È fantastico. +1. – strager

0

Si può mettere il corpo del vostro loop in una funzione anonima:

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](); 
} 

Con la creazione di quella funzione e passando il valore del ciclo di "i" come argomento, stiamo creando una nuova variabile "i" all'interno del corpo del loop che nasconde essenzialmente la "i" esterna. La chiusura che si preme sulla matrice ora vede la nuova variabile, il cui valore è impostato quando la funzione di funzione esterna viene chiamata nel primo ciclo. Questo potrebbe essere più chiaro se si usa un nome diverso quando creiamo la nuova variabile ... Questo fa la stessa cosa:

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](); 
} 

Il valore di "INEW" è assegnato 0, poi 1, poi 2 perché la funzione è chiamato immediatamente dal ciclo.