2009-09-12 6 views
6

Considérez ce qui suit le code Javascript:javascript fermeture évaluation immédiate

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

Les alertes imprimer sur '3' trois fois. Je veux un comportement différent - dans chaque itération de la boucle générer une fonction qui imprime la valeur actuelle de i. C'est à dire. 3 fonctions qui impriment différents index.

Des idées?

+0

Juste pour ajouter c'est à cause de Javascript n'a pas de concept de portée de bloc seulement la portée de fonction, ceci m'a jeté aussi ... http://www.mattfreeman.co.uk/2010/03/closures-scope-in- javascript-vs-c/ –

Répondre

7

Créer une fonction anonyme qui accepte i comme paramètre et retourne cette fonction particulière:

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

Ou quelque chose de similaire: créer une fonction anonyme qui accepte i comme paramètre pour ajouter la fonction au tableau:

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

Pas que ce soit nécessaire, mais je pense que ça a l'air plus propre, et décrit mieux vos intentions pour envelopper vos fonctions "immédiatement exécutées" dans '()' -> '(function (i) {...}) (i); ' – gnarf

+0

@gnarf, j'en discutais moi-même. Je suppose que cela rend l'intention plus claire. Je vais éditer cela dans. – strager

+0

cela semble contourner le problème original en offrant une solution alternative qui n'est pas sensible aux mêmes failles sous-jacentes ... Ce que vous faites ici est de pousser des valeurs sur un tableau. L'affiche originale pousse des fonctions, qui, nous présumons, doivent être exécutées plus tard ... – Funka

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

Vous n'avez pas besoin de fermeture à une valeur de sortie uniquement. Votre code devrait, cependant, être contenu dans une fonction de confinement orienté objet. Les fonctions n'ont pas besoin d'être appelées pour être exécutées.

+0

"Les fonctions n'ont pas besoin d'être appelées pour être exécutées." Quelle? Cette affirmation n'est pas très claire et, comme elle l'est, elle semble fausse. Clarifier, s'il te plait? – strager

+0

Pour exécuter une fonction, l'une des trois choses doit se produire. 1) La fonction doit être appelée par son nom par un autre élément en cours d'exécution. 2) La fonction peut être insérée dans une méthode, auquel cas la fonction peut être anonyme et être toujours exécutée. Je m'objecte fortement à utiliser des fonctions sans leur donner de nom. 3) Les fonctions peuvent s'exécuter entièrement seules lorsqu'elles sont interprétées si elles sont terminées par une parenthèse après leur parenthèse fermante, par exemple}(). C'est ce qu'on appelle l'évocation immédiate. –

+0

Je ne suis pas d'accord. Une fonction est un type de valeur, tout comme '42' ou' 'hello world'', en Javascript. Qu'il soit assigné à une variable ou utilisé directement ne signifie rien de spécial. Pour un exemple de ce comportement, exécutez: '(fonction (i) {var func = arguments.callee; si (! I) renvoie; console.log ('x'); window.setTimeout (fonction() {func (i - 1);}, 1000);}) (4); ' – strager

-2
fonction

(i) {alert (i)

+2

Plus que probablement, «je» sera indéfini. – strager

+0

+1 pour la finesse. Cela fonctionnera si a.push (fonction (i) {alert (i)}); est utilisé à la place de a.push (function() {alert (i)}); –

6

Juste une autre approche, en utilisant 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))); 
    }; 
}; 

Vérifiez l'extrait ci-dessus en cours d'exécution here.

+1

Belle utilisation de curry - bien que maintenant j'ai faim .... – gnarf

+0

Wow. C'est génial. +1 – strager

0

Vous pouvez mettre le corps de votre boucle dans une fonction anonyme:

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

En créant cette fonction et passer la valeur de la boucle de « i » comme argument, nous créons une nouvelle variable « i » à l'intérieur du corps de la boucle qui cache essentiellement le "i" externe. La fermeture que vous poussez sur le tableau voit maintenant la nouvelle variable, dont la valeur est définie lorsque la fonction de fonction externe est appelée dans la première boucle. Cela pourrait être plus clair si nous utilisons un nom différent lorsque nous créons la nouvelle variable ... Cela fait la même chose:

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

La valeur de « iNew » est attribué 0, 1, puis 2 parce que la fonction est appelé immédiatement par la boucle.

Questions connexes