2
votes

JavaScript pour boucle avec let

Mon livre JavaScript, "JavaScript The Definitive Guide, 6th Ed.", page 270 inclut ce texte et ce code:

"... dans une boucle for, l'expression d'initialisation est évaluée en dehors de la portée de la nouvelle variable "

let x = 1;
for (let x = x + 1; x < 5; x++) {
    console.log(x); // prints 2, 3, 4
}

Lorsque j'exécute le code ci-dessus (dans la dernière version de Chrome et FF) cependant, j'obtiens des erreurs de console:

ReferenceError: x n'est pas défini

impossible d'accéder à la déclaration lexicale `x 'avant l'initialisation

Le code du livre est-il incorrect? (Il n'y a rien sur le site d'errata du livre concernant: ceci.)


5 commentaires

x est redéclaré , c'est le problème.


@briosheje non, il n'est pas redéclaré. Il s'obscurcit.


@JonasWilms est vrai, en effet. Bien que le problème soit toujours là. Il est masqué par la variable locale x , non?


@briosheje le problème essaie d'accéder à une variable à l'intérieur de son initialiseur. Le paragraphe cité est tout simplement faux.


Merci tout le monde! Cela a plus de sens pour moi maintenant. J'ai maintenant une BEAUCOUP mieux comprendre comment se comporte le mot-clé let!


5 Réponses :


4
votes

Le seul problème est que x est masqué (comme mentionné par Jonas ci-dessus), d'où cela génère une erreur.

Supprimez simplement le deuxième let et tout fonctionnera comme prévu.

let x = 1;
for (x = x + 1; x < 5; x++) {
   //^---- note the missing "let" here. 
   console.log(x); // prints 2, 3, 4
}

Si vous l'avez copié à partir d'un livre, alors c'est un problème de livre.

https://jsfiddle.net/hto9udmj/

De plus amples informations sur les déclarations de variables peuvent être trouvées ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Plus d'infos sur l'observation des variables: Un exemple d'ombrage de variable en javascript

p >


0 commentaires

2
votes

Vous initialisez x deux fois et obtenez ainsi une erreur. Renommez un x en i

​​

let x = 1;
for (let i = x + 1; i < 5; i++) {
    console.log(i); // prints 2, 3, 4
}


0 commentaires

0
votes

Le problème est que vous redéclarez x dans la boucle for et puisque let n'existe que dans le contexte donné, une fois la boucle terminée x n'existe plus.

Déclarez x une fois en dehors de la boucle for , ou utilisez var code >. var ajoute la variable à une portée globale, donc elle existera une fois la boucle for terminée.

for (var x = 2; x < 5; x++) {}
console.log(x);

Avec var:

let x = 1;
for (x = x + 1; x < 5; x++) {}
console.log(x);


0 commentaires

5
votes

Le problème n'est pas vraiment que x soit déclaré deux fois. C'est juste que vous essayez d'accéder au x intérieur avant qu'il ne soit initialisé:

 for(let a = 1, b = a; ; )

S'il y a un autre x à l'extérieur portée (les initialiseurs dans une boucle for sont dans leur propre portée) n'a pas d'importance, le x fera référence à la variable dans la portée actuelle, car elle a déjà été déclarée ( en raison du levage), mais n'a pas encore été initialisé:

 let x = 0; // irrelevant

 { // x gets declared as part of this scope
   x; // thats an error too as x is not initialized yet
   let x = 1; // initialization
   x; // now it can be accessed
 }

La partie entre le début d'une portée et une déclaration let est appelée " zone morte temporelle "...

"... dans une boucle for, l'expression d'initialisation est évaluée en dehors de la portée de la nouvelle variable"

Non, sinon vous ne pourriez pas faire référence à d'autres variables dans l'initialiseur:

 let x = x /* doesn't exist yet*/;
 

Comme toujours, la réponse définitive se trouve dans la spécification:

13.7.4.7 Sémantique d'exécution: évaluation étiquetée

IterationStatement: for (LexicalDeclaration Expression; Expression) Statement

  1. Soit oldEnv l'environnement lexical du contexte d'exécution en cours d'exécution.

  2. Soit loopEnv NewDeclarativeEnvironment (oldEnv).

[...]

  1. Soit boundNames les BoundNames de LexicalDeclaration.

  2. Pour chaque élément dn de boundNames [..]

    Jouez! loopEnvRec.CreateImmutableBinding (dn, true).

  3. Définissez le LexicalEnvironment du contexte d'exécution en cours sur loopEnv.

  4. Soit forDcl le résultat de l'évaluation de la déclaration lexicale.

[...]

Comme vous pouvez le voir, le contexte d'exécution en cours est loopEnv tandis que la déclaration lexicale (les initialiseurs) est évaluée, pas oldEnv .

TLDR: Non seulement l'exemple est faux, mais aussi le paragraphe.


0 commentaires

3
votes

Le code du livre est-il incorrect? (Il n'y a rien sur le site d'errata du livre concernant ceci.)

Je pense que les livres étaient corrects; lorsque let a été introduit pour la première fois il y a des années dans Firefox.

Plus précisément, il n'avait pas le temporal dead zone , et il se comporte en interne plus comme var , il suffit de bloquer portée.

Dans Firefox 44, il y a eu un changement radical qui fait que let et const respectent les normes:

https: //blog.mozilla .org / addons / 2015/10/14 / broken-changes-let-const-firefox-nightly-44 /

Y compris l'introduction de la zone morte temporelle.

Donc, oui, le livre maintenant est incorrect; puisque vous essayez de faire quelque chose comme:
let x = 0;
{
  let y = x; // `let` is block-scope,
             // so this `x` is actually the `x` 
             // defined below, not the one outside
             // the scope, hence the `ReferenceError`.
  let x = 1;
}

2 commentaires

Sans la zone morte temporelle pour laquelle il (let x = x serait simplement indéfini , l'évaluation des initialiseurs de boucle for doit également avoir changé


Comme dit, let était juste vu en interne comme un var avec des fonctionnalités de type bloc. Plus précisément, dans le cas de la boucle, let x = x + 1 aurait pris le x (de x + 1 ) de l'extérieur du portée, car il n'a pas encore été déclaré. Je ne dis pas que c'est un bon comportement, je dis simplement que c'est ainsi que la première implémentation de let fonctionnait. Vous pouvez vous tester avec une version de Firefox qui implémente le "legacy" let : dans ce cas, le code dans le livre était "correct" (dans le sens qui fonctionne comme le livre le précise).