web-dev-qa-db-fra.com

Les variables déclarées avec let ou const ne sont-elles pas levées dans ES6?

Je joue avec ES6 depuis un moment et j'ai remarqué que, bien que les variables déclarées avec var soient levées comme prévu ...

console.log(typeof name); // undefined
var name = "John";

... les variables déclarées avec let ou const semblent rencontrer quelques problèmes de levage:

console.log(typeof name); // ReferenceError
let name = "John";

et

console.log(typeof name); // ReferenceError
const name = "John";

Cela signifie-t-il que les variables déclarées avec let ou const ne sont pas levées? Qu'est-ce qui se passe vraiment ici? Existe-t-il une différence entre let et const en la matière?

227
Luboš Turek

@thefourtheye a raison de dire que ces variables inaccessibles avant d'être déclarées. Cependant, c'est un peu plus compliqué que cela.

Les variables déclarées avec let ou const ne sont-elles pas levées? Qu'est-ce qui se passe vraiment ici?

Toutes les déclarations (var, let, const, function, function*, class) sont "hissés" en JavaScript. Cela signifie que si un nom est déclaré dans une étendue, l'identifiant référencera toujours cette variable particulière:

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

Ceci est vrai à la fois pour les portées de fonction et de bloc1.

La différence entre var/function/function* déclarations et let/const/class déclarations est le initialisation .
Les premiers sont initialisés avec undefined ou la fonction (générateur) dès que la liaison est créée en haut de la portée. Les variables déclarées lexicalement restent cependant non initialisées. Cela signifie qu'une exception ReferenceError est générée lorsque vous essayez d'y accéder. Il ne sera initialisé que lorsque l'instruction let/const/class sera évaluée, tout ce qui précède (ci-dessus) est appelé la zone morte temporelle .

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

Notez qu'une instruction let y; initialise la variable avec undefined comme let y = undefined;.

La zone morte temporelle n'est pas un emplacement syntaxique, mais plutôt le temps entre la création de la variable (scope) et l’initialisation. Ce n’est pas une erreur de référencer la variable dans le code au-dessus de la déclaration tant que ce code n’est pas exécuté (par exemple, un corps de fonction ou simplement un code mort), et une exception sera levée si vous accédez à la variable avant l’initialisation, même si Le code est en dessous de la déclaration (par exemple dans une déclaration de fonction levée appelée trop tôt).

Existe-t-il une différence entre let et const en la matière?

Non, ils fonctionnent de la même manière en ce qui concerne le levage. La seule différence entre eux est qu'un constant doit être et ne peut être affecté que dans la partie initialisation de la déclaration (const one = 1;, const one; et des réassignements ultérieurs tels que one = 2 sont invalide).

1: var les déclarations ne fonctionnent toujours qu'au niveau de la fonction, bien sûr

305
Bergi

Citant la spécification de la spécification ECMAScript 6 (ECMAScript 2015), let et const déclarations section,

Les variables sont créées lorsque leur environnement lexical contenant est instancié, mais il est impossible d’accéder tant que la variable LexicalBinding n’est pas évaluée .

Donc, pour répondre à votre question, oui, let et const palan, mais vous ne pouvez pas y accéder avant que la déclaration proprement dite ne soit évaluée au moment de l'exécution.

76
thefourtheye

ES6 introduit les variables Let qui apparaissent avec block level scoping. Jusqu'au ES5 nous n'avions pas block level scoping, donc les variables déclarées à l'intérieur d'un bloc sont toujours hoisted au niveau de la portée.

Fondamentalement, Scope fait référence à l'endroit où, dans votre programme, vos variables sont visibles, ce qui détermine où vous êtes autorisé à utiliser des variables que vous avez déclarées. Dans ES5 nous avons global scope,function scope and try/catch scope, avec ES6, nous obtenons également la portée du niveau de bloc en utilisant Let.

  • Lorsque vous définissez une variable avec le mot clé var, la fonction entière est connue à partir du moment où elle est définie.
  • Lorsque vous définissez une variable avec l'instruction let, elle n'est connue que dans le bloc défini.

     function doSomething(arr){
         //i is known here but undefined
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(var i=0; i<arr.length; i++){
             //i is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(let j=0; j<arr.length; j++){
             //j is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
     }
    
     doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);
    

Si vous exécutez le code, vous constaterez que la variable j n'est connue que dans loop et non avant et après. Cependant, notre variable i est connue dans le entire function à partir du moment où elle est définie.

Il y a un autre grand avantage à utiliser let as it crée un nouvel environnement lexical et lie également une nouvelle valeur plutôt que de conserver une ancienne référence.

for(var i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

for(let i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

La première boucle for affiche toujours la dernière valeur , avec let elle crée une nouvelle portée et lie les nouvelles valeurs en nous imprimant 1, 2, 3, 4, 5.

En venant de constants, cela fonctionne fondamentalement comme let, la seule différence est que leur valeur ne peut pas être modifiée. En constantes la mutation est autorisée mais la réaffectation est interdite.

const foo = {};
foo.bar = 42;
console.log(foo.bar); //works

const name = []
name.Push("Vinoth");
console.log(name); //works

const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.

console.log(age);

Si une constante fait référence à une object, elle fera toujours référence à la object, mais la object elle-même peut être modifiée (si elle est mutable). Si vous aimez avoir un object immuable, vous pouvez utiliser Object.freeze([])

21
Thalaivar

De documents Web MDN:

Dans ECMAScript 2015, let et const sont levés mais non initialisés. Référencer la variable dans le bloc avant la déclaration de variable a pour résultat un ReferenceError car la variable est dans une "zone morte temporelle" à partir du début du bloc jusqu'à ce que la déclaration soit traitée.

console.log(x); // ReferenceError
let x = 3;
1
YourAboutMeIsBlank