Le variabili dichiarate con let o const non sono issate in ES6?


266

Ho giocato con ES6 per un po 'e ho notato che mentre le variabili dichiarate con varsono sollevate come previsto ...

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

... le variabili dichiarate con leto constsembrano avere problemi con il sollevamento:

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

e

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

Questo significa che le variabili dichiarate con leto constnon vengono sollevate? Cosa sta succedendo davvero qui? C'è qualche differenza tra lete constin questa materia?

Risposte:


346

@thefourtheye ha ragione nel dire che non è possibile accedere a queste variabili prima che vengano dichiarate. Tuttavia, è un po 'più complicato di così.

Le variabili sono dichiarate con leto constnon issate? Cosa sta succedendo davvero qui?

Tutte le dichiarazioni ( var, let, const, function, function*, class) sono "issato" in JavaScript. Ciò significa che se un nome viene dichiarato in un ambito, in tale ambito l'identificatore farà sempre riferimento a quella particolare variabile:

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

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

    let/const/… x;
}

Questo vale sia per gli ambiti di funzione che di blocco 1 .

La differenza tra var/ function/ function*dichiarazioni e let/ const/ classdichiarazioni è l' inizializzazione .
I primi vengono inizializzati con undefinedo la funzione (generatore) proprio quando viene creata la rilegatura nella parte superiore dell'ambito. Le variabili dichiarate lessicalmente rimangono tuttavia non inizializzate . Ciò significa che ReferenceErrorviene generata un'eccezione quando si tenta di accedervi. Verrà inizializzato solo quando viene valutata l'istruzione let/ const/ class, tutto prima (sopra) che viene chiamato zona morta temporale .

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

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

Si noti che let y;un'istruzione inizializza la variabile conundefined like let y = undefined;.

La zona morta temporale non è un luogo sintattico, ma piuttosto il tempo intercorre tra la creazione della variabile (ambito) e l'inizializzazione. Non è un errore fare riferimento alla variabile nel codice sopra la dichiarazione fintanto che quel codice non viene eseguito (ad esempio un corpo della funzione o semplicemente un codice morto) e genererà un'eccezione se si accede alla variabile prima dell'inizializzazione anche se l'accesso il codice è al di sotto della dichiarazione (ad es. in una dichiarazione di funzione sollevata chiamata troppo presto).

C'è qualche differenza tra leteconst in questa materia?

No, funzionano allo stesso modo per quanto riguarda il sollevamento. L'unica differenza tra loro è che una constformica deve essere e può essere assegnata solo nella parte di inizializzazione della dichiarazione ( const one = 1;sia le const one;riassegnazioni sia quelle successive one = 2non sono valide).

1: le vardichiarazioni funzionano ancora solo a livello di funzione, ovviamente


16
Trovo che qualcosa di simile let foo = () => bar; let bar = 'bar'; foo();illustri ancora meglio l'effetto di tutte le dichiarazioni , perché non è ovvio a causa della zona morta temporale.
Estus Flask,

1
Stavo per chiedere di fare riferimento a una definizione let in una funzione dichiarata prima di let (cioè una chiusura). Penso che questo risponda alla domanda, è legale ma sarà un errore ref se la funzione viene invocata prima dell'esecuzione dell'istruzione let, e andrà bene se la funzione viene invocata in seguito. forse questo potrebbe essere aggiunto alla risposta se vero?
Mike Lippert,

2
@MikeLippert Sì, è corretto. Non è necessario chiamare la funzione che accede alla variabile prima che sia inizializzata. Questo scenario si verifica con ogni dichiarazione di funzione sollevata, ad esempio.
Bergi,

1
La decisione di fare constcome letè un difetto di progettazione. In un ambito, constavrebbe dovuto essere sollevato e inizializzato just-in-time quando vi si accede. In realtà, dovrebbero avere una const, una lete un'altra parola chiave che crea una variabile che funziona come un "sola lettura" let.
Pacerier,

1
" I primi sono inizializzati con undefined ..." potrebbe essere ok per le dichiarazioni var ma non sembra appropriato per le dichiarazioni di funzioni, a cui è assegnato un valore prima che inizi l'esecuzione.
RobG

87

Citando le specifiche e le dichiarazioni ECMAScript 6 (ECMAScript 2015)letconst sezione ,

Le variabili vengono create quando viene istanziato il loro ambiente Lexical contenente ma non è possibile accedervi in ​​alcun modo fino a quando non viene valutato LexicalBinding della variabile .

Quindi, per rispondere alla tua domanda, sì, lete constparanco ma non puoi accedervi prima che la dichiarazione effettiva venga valutata in fase di esecuzione.


22

ES6introduce le Letvariabili che ne risultano block level scoping. Fino a ES5quando non lo avevamo block level scoping, quindi le variabili dichiarate all'interno di un blocco sono semprehoisted funzionare a livello di scoping.

Fondamentalmente si Scoperiferisce a dove nel tuo programma sono visibili le tue variabili, che determina dove ti è permesso usare le variabili che hai dichiarato. In ES5abbiamo global scope,function scope and try/catch scope, con ES6otteniamo anche l'ambito scoping a livello di blocco usando Let.

  • Quando si definisce una variabile con varparola chiave, è nota l'intera funzione dal momento in cui è stata definita.
  • Quando si definisce una variabile con letistruzione, è nota solo nel blocco in cui è definita.

     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"]);

Se esegui il codice, potresti vedere che la variabile jè nota solo nel loope non prima e dopo. Tuttavia, la nostra variabile iè nota inentire function dal momento in cui è definita in poi.

C'è un altro grande vantaggio nell'usare let in quanto crea un nuovo ambiente lessicale e lega anche un nuovo valore piuttosto che mantenere un vecchio riferimento.

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)
}

Il primo forciclo stampa sempre l' ultimo valore, con letesso crea un nuovo ambito e lega nuovi valori stampandoci 1, 2, 3, 4, 5.

Venendo a constants, funziona praticamente come let, l'unica differenza è che il loro valore non può essere modificato. Nelle costanti è consentita la mutazione ma non è consentita la riassegnazione.

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);

Se una costante si riferisce a una object, farà sempre riferimento a objectma la objectstessa può essere modificata (se è mutabile). Se ti piace avere un immutabile object, potresti usarloObject.freeze([])


5

Dai documenti web MDN:

In ECMAScript 2015, lete constsono issato, ma non inizializzato. Il riferimento alla variabile nel blocco prima della dichiarazione della variabile determina un ReferenceErrorperché la variabile si trova in una "zona morta temporale" dall'inizio del blocco fino a quando la dichiarazione non viene elaborata.

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

0

in es6 quando usiamo let o const dobbiamo dichiarare la variabile prima di usarle. per esempio. 1 -

// this will work
u = 10;
var u;

// this will give an error 
k = 10;
let k;  // ReferenceError: Cannot access 'k' before initialization.

per esempio. 2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.