2015-07-04 11 views
149

Ho giocato con ES6 per un po 'e ho notato che mentre le variabili dichiarate con var vengono issate come previsto ...Le variabili dichiarate con let o const non vengono issate in ES6?

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

... variabili dichiarate con let o const sembrano avere alcuni problemi con sollevamento :

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

e

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

questo mi una variabile dichiarata con let o const non viene issata? Cosa sta succedendo davvero qui? C'è qualche differenza tra let e const in questa materia?

risposta

197

@thefourtheye ha ragione dicendo che queste variabili non sono accessibili prima che vengano dichiarate. Tuttavia, è un po 'più complicato di così.

Le variabili dichiarate con let o const non vengono 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 è dichiarata in un ambito, in tale ambito l'identificatore farà sempre riferimento tale 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 è vero sia per funzioni e blocchi ambiti .

La differenza tra var/function/function* dichiarazioni e let/const/class DICHIARAZIONE ­ zioni è il inizializzazione.
I primi sono inizializzati con undefined o la funzione (generatore) destra quando l'associazione viene creata nella parte superiore dell'ambito. Le variabili dichiarate in modo lessicale rimangono tuttavia non inizializzate. Ciò significa che viene generata un'eccezione ReferenceError quando si tenta di accedervi.Verrà inizializzato solo quando viene valutata l'istruzione let/const/class, tutto prima (sopra) che è chiamato la zona zona morta.

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

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

Si noti che una dichiarazione let y; inizializza la variabile con undefined come let y = undefined; avrebbe.

La zona morta temporale non è una posizione sintattica, ma piuttosto il tempo tra il (portata) creazione variabile e l'inizializzazione. Non è un errore fare riferimento alla variabile nel codice sopra la dichiarazione fintanto che quel codice non viene eseguito (es. Un corpo funzione o semplicemente 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 una funzione issata chiamata troppo presto).

C'è qualche differenza tra let e const in questa materia?

No, funzionano lo stesso per quanto riguarda il sollevamento. L'unica differenza tra loro è che un form const deve essere e può essere assegnato solo nella parte inizializzatore della dichiarazione (const one = 1;, entrambi const one; e successive riassegnazioni come one = 2 non sono valide).

1: var dichiarazioni stanno ancora lavorando solo a livello di funzione, ovviamente

+0

@janaspage: Questo è quello che dice la risposta, vero? Btw, tutto tranne le dichiarazioni 'var' sono issate sullo scope block, non solo' let'. – Bergi

+0

Ah, era implicito. Il sollevamento avviene sempre all'interno di un ambito e i blocchi sono l'ambito di tutto (tranne 'var'). – Bergi

+4

Trovo che qualcosa come 'let foo =() => bar; let bar = 'bar'; foo(); 'illustra * tutte le dichiarazioni sono issate * l'effetto è ancora migliore, perché non è ovvio a causa della zona morta temporale. – estus

53

Citando ECMAScript 6 (ECMAScript 2015) specificazione di, let and const declarations sezione

Le variabili sono create quando la loro contenente lessicale ambiente viene istanziata ma non può accedere in alcun modo fino LexicalBinding della variabile viene valutata.

Quindi, per rispondere alla tua domanda, sì, let e const paranco, ma non è possibile accedervi prima della dichiarazione reale è valutata in fase di esecuzione.

14

ES6 introduce Let variabili che esce con block level scoping. Fino al ES5 non disponevamo dello block level scoping, quindi le variabili dichiarate all'interno di un blocco sono sempre hoisted per l'ambito del livello di funzione.

Fondamentalmente Scope fa riferimento a dove nel programma sono visibili le variabili, che determina dove è consentito utilizzare le variabili dichiarate. In ES5 abbiamo global scope,function scope and try/catch scope, con ES6 otteniamo anche l'ambito del livello di blocco utilizzando Let.

  • Quando si definisce una variabile con la parola chiave var, è nota l'intera funzione dal momento in cui viene definita.
  • Quando si definisce una variabile con l'istruzione let, è nota solo nel blocco definito.

    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 si esegue il codice, si poteva vedere la variabile j è conosciuto solo nel loop e non prima e dopo. Tuttavia, la nostra variabile i è nota nel entire function dal momento in cui è definita in avanti.

C'è un altro grande vantaggio nell'utilizzo di let in quanto crea un nuovo ambiente lessicale e associa 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 ciclo for stampare sempre l'ultimo valore , con let crea un nuovo ambito e associare valori freschi di stampa noi 1, 2, 3, 4, 5.

Venendo a constants, funziona fondamentalmente come let, l'unica differenza è che il loro valore non può essere modificato. Nella costante è 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 ad un object, sarà sempre riferimento al object ma il object stessa può essere modificata (se è mutevole). Se ti piace avere un object immutabile, potresti usare Object.freeze([])