web-dev-qa-db-fra.com

laisser le mot clé dans la boucle for

let d'ECMAScript 6 est censé fournir une portée de bloc sans lever les maux de tête. Certains peuvent-ils expliquer pourquoi dans le code ci-dessous i dans la fonction se résout à la dernière valeur de la boucle (tout comme avec var) au lieu de la valeur de l'itération actuelle?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    things["fun" + i] = function() {
        console.log(i);
    };
}

things["fun0"](); // prints 3
things["fun1"](); // prints 3
things["fun2"](); // prints 3

Selon MDN en utilisant let dans la boucle for comme cela devrait lier la variable dans la portée du corps de la boucle. Les choses fonctionnent comme je m'y attendais lorsque j'utilise une variable temporaire à l'intérieur du bloc. Pourquoi est-ce nécessaire?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    let index = i;
    things["fun" + i] = function() {
        console.log(index);
    };
}

things["fun0"](); // prints 0
things["fun1"](); // prints 1
things["fun2"](); // prints 2

J'ai testé le script avec Traceur et node --harmony.

41

la réponse de strabisme n'est plus à jour. Dans spécification ECMA 6 , le comportement spécifié est que dans

for(let i;;){}

i obtient une nouvelle liaison pour chaque itération de la boucle.

Cela signifie que chaque fermeture capture une instance i différente. Donc, le résultat de 012 est le résultat correct à partir de maintenant. Lorsque vous l'exécutez dans Chrome v47 +, vous obtenez le résultat correct. Lorsque vous l'exécutez dans IE11 et Edge, actuellement le résultat incorrect (333) semble être produit.

Plus d'informations sur ce bogue/fonctionnalité peuvent être trouvées dans les liens dans cette page ;

Depuis que l'expression let est utilisée, chaque itération crée une nouvelle portée lexicale chaînée à la portée précédente. Cela a des implications sur les performances pour l'utilisation de l'expression let, qui est signalée ici .

50
neuron

J'ai transmis ce code via Babel afin que nous puissions comprendre le comportement en termes d'ES5 familier:

for (let i = 0; i < 3; i++) {
    i++;
    things["fun" + i] = function() {
        console.log(i);
    };
    i--;
}

Voici le code transposé en ES5:

var _loop = function _loop(_i) {
    _i++;
    things["fun" + _i] = function () {
        console.log(_i);
    };
    _i--;
    i = _i;
};

for (var i = 0; i < 3; i++) {
    _loop(i);
}

Nous pouvons voir que deux variables sont utilisées.

  • Dans la portée externe i est la variable qui change au fur et à mesure de l'itération.

  • Dans la portée interne _i Est une variable unique pour chaque itération. Il y aura finalement trois instances distinctes de _i.

    Chaque fonction de rappel peut voir son _i Correspondant, et peut même le manipuler si elle le souhaite, indépendamment des _i S dans d'autres étendues.

    (Vous pouvez confirmer qu'il existe trois _i S différents en faisant console.log(i++) dans le rappel. La modification de _i Dans un rappel antérieur n'affecte pas la sortie des rappels ultérieurs.)

À la fin de chaque itération, la valeur de _i Est copiée dans i. Par conséquent, la modification de la variable interne unique pendant l'itération affectera la variable itérée externe.

Il est bon de voir que ES6 a poursuivi la longue tradition de WTFJS.

14
joeytwiddle

À mon humble avis - les programmeurs qui ont d'abord mis en œuvre ce LET (produisant les résultats de votre version initiale) l'ont fait correctement en ce qui concerne la raison; ils peuvent ne pas avoir jeté un coup d'œil à la spécification au cours de cette mise en œuvre.

Il est plus logique qu'une seule variable soit utilisée, mais portée à la boucle for. D'autant plus que l'on devrait se sentir libre de changer cette variable selon les conditions dans la boucle.

Mais attendez - vous pouvez changer la variable de boucle. WTFJS !! Cependant, si vous essayez de le changer dans votre portée interne, cela ne fonctionnera pas maintenant car c'est une nouvelle variable.

Je n'aime pas ce que je dois faire Pour obtenir ce que je veux (une seule variable locale au for):

{
    let x = 0;
    for (; x < length; x++)
    {
        things["fun" + x] = function() {
            console.log(x);
        };
    }
}

Où modifier la version plus intuitive (si imaginaire) pour gérer une nouvelle variable par itération:

for (let x = 0; x < length; x++)
{
    let y = x;
    things["fun" + y] = function() {
        console.log(y);
    };
}

Il est clair que mon intention avec la variable y est .. Ou aurait été si SANITY gouvernait l'univers.

Donc, votre premier exemple fonctionne maintenant dans FF; il produit le 0, 1, 2. Vous pouvez appeler le problème résolu. J'appelle le problème WTFJS.

ps. Ma référence à WTFJS est de JoeyTwiddle ci-dessus; Cela ressemble à un mème que j'aurais dû connaître avant aujourd'hui, mais aujourd'hui était le moment idéal pour l'apprendre.

1
Gerard ONeill