Domanda

Introduzione di ECMAScript 6 IL let dichiarazione.

Ho sentito dire che è descritta come una variabile "locale", ma non sono ancora del tutto sicuro di come si comporti diversamente rispetto a var parola chiave.

Quali sono le differenze?Quando dovrebbe let essere utilizzato var?

È stato utile?

Soluzione

regole

di ambito

La differenza principale è scoping regole. Le variabili dichiarate da var parola ha come ambito corpo funzione immediata (da qui la funzione portata) mentre le variabili let vengono ambiti all'immediata racchiude blocco indicato con { } (quindi, la portata di blocco).

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar);

  {
    let baz = "Bazz";
    console.log(baz);
  }

  console.log(baz); // ReferenceError
}

run();

Il motivo per cui parola chiave let è stato introdotto per la lingua era portata funzione è confusa e fu uno dei principale fonte di bug in javascript.

Date un'occhiata a questo esempio da un'altra domanda StackOverflow :

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3 era uscita di consolare ogni volta funcs[j](); è stata invocata in quanto funzioni anonime erano legati alla stessa stessa variabile.

La gente doveva creare creare funzioni immediatamente invocate per catturare valore corretto dai loop ma che era anche peloso.

Sollevamento

Mentre variabili dichiarate con var parola chiave sono "sollevati" per la parte superiore del blocco che significa che sono accessibili nella loro portata che racchiude anche prima che siano dichiarati:

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

lasciare che le variabili non sono inizializzate finché non viene valutata la loro definizione. accedervi prima che i risultati di inizializzazione in un ReferenceError. Variabile detto di essere in "zona morta temporale" dall'inizio del blocco finché l'inizializzazione viene elaborato.

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

La creazione di proprietà oggetto globale

Al livello superiore, let, a differenza var, non crea una proprietà sull'oggetto globale:

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

ridichiarazione

In modalità rigorosa, var vi permetterà di ri-dichiara la stessa variabile nello stesso ambito, mentre let solleva uno SyntaxError.

'use strict';
var foo = "foo1';
var foo = "foo2'; // No problem, 'foo' is replaced.

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

Altri suggerimenti

let può essere utilizzato anche per evitare problemi con le chiusure. Si lega il valore fresco, piuttosto che mantenere un vecchio di riferimento, come illustrato negli esempi che seguono.

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

Codice di cui sopra dimostra un classico problema JavaScript chiusura. Riferimento alla variabile i viene memorizzato nella chiusura a scatto gestore, piuttosto che il valore effettivo della i.

Ogni singolo gestore clic si riferiscono allo stesso oggetto, perché c'è un solo oggetto contatore che tiene 6 in modo da ottenere sei per ogni clic.

Una soluzione generale è quello di avvolgere questo in una funzione anonima e passare i come argomento. Tali problemi possono essere evitati ora utilizzando let invece var come mostrato nel seguente codice.

(Testato in Chrome e Firefox 50)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

Qual è la differenza tra let e var?

  • Una variabile definita utilizzando un'istruzione var è conosciuta in tutto la funzione è definito nella, fin dall'inizio della funzione. *
  • Una variabile definita utilizzando un'istruzione let è noto solo in < strong> blocco è definita, dal momento in cui viene definito in avanti. **

Per capire la differenza, si consideri il seguente codice:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Qui, possiamo vedere che la nostra j variabile è conosciuta solo nel primo ciclo for, ma non prima e dopo. Eppure, il nostro i variabile è conosciuto in tutta la funzionalità.

Inoltre, si consideri che le variabili di blocco ambito non sono noti prima di essere dichiarate, perché non sono issate. Non sei anche permesso di ridichiarare stesso blocco ambito variabile all'interno dello stesso blocco. Questo rende blocco variabili con scope meno soggetta ad errori di variabili ambito globale o funzionalmente, che vengono issati e che non producono errori nel caso di dichiarazioni multiple.


E 'sicuro da usare let oggi?

Alcune persone sostengono che in futuro avremo SOLO utilizziamo lasciare dichiarazioni e che le dichiarazioni var diventerà obsoleto. JavaScript guru Kyle Simpson ha scritto un articolo molto elaborato sul perché egli ritiene che non sarà il caso .

Oggi, tuttavia, che non è sicuramente il caso. In realtà, abbiamo bisogno in realtà chiederci se è sicuro di utilizzare l'istruzione let. La risposta a questa domanda dipende dall'ambiente:

  • Se si scrive sul lato server codice JavaScript ( Node.js ), si può tranquillamente utilizzare l'istruzione let.

  • Se si scrive JavaScript lato client codice e utilizzare un transpiler basato su browser (come < strong> Traceur o href="https://github.com/babel/babel-standalone" rel="noreferrer"> babel-alone ), si può tranquillamente utilizzare l'istruzione let, tuttavia il codice è probabile che sia tutt'altro che ottimale rispetto alle prestazioni.

  • Se si scrive JavaScript lato client il codice e utilizzare un transpiler base Node (come il traceur script di shell o href="https://babeljs.io/" rel="noreferrer"> Babel ), si può tranquillamente utilizzare l'istruzione let. E perché il browser verrà a conoscenza del codice transpiled, svantaggi prestazioni dovrebbero essere limitate.

  • Se si scrive sul lato client il codice JavaScript e non utilizzare un transpiler, è necessario considerare il supporto del browser.

    Ci sono ancora alcuni browser che non supportano let a tutti:

 tavolo Support


Come tenere traccia di supporto del browser

Per una panoramica up-to-date, di cui i browser supportano la dichiarazione let al momento della vostra lettura questa risposta, vedere questa pagina Can I Use .


* va a livello globale e funzionalmente ambitoVARIABILI possono essere inizializzati e utilizzati prima di essere dichiarati perché le variabili JavaScript sono issato . Questo significa che le dichiarazioni sono sempre molto per la parte superiore del campo di applicazione.

** Blocca ambito variabili non vengono issate

Ecco un del let parola chiave con alcuni esempi.

  

let funziona come var. La differenza principale è che la portata di una variabile var rappresenta l'intera funzione racchiude

Questo tavolo su Wikipedia indica che i browser supporta Javascript 1.7.

Si noti che solo i browser Mozilla e Chrome lo supportano. IE, Safari, e potenzialmente altri no.

La risposta accettata manca un punto:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined

let

Blocca l'ambito

Variabili dichiarate utilizzando il file let hanno un ambito di blocco, il che significa che sono disponibili solo in bloccare in cui sono stati dichiarati.

Al livello più alto (all'esterno di una funzione)

Al livello più alto, le variabili dichiarate utilizzando let non creare proprietà sull'oggetto globale.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

All'interno di una funzione

All'interno di una funzione (ma all'esterno di un blocco), let ha lo stesso ambito di var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

All'interno di un isolato

Variabili dichiarate utilizzando let all'interno di un blocco non è possibile accedere all'esterno di quel blocco.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

All'interno di un ciclo

Variabili dichiarate con let in loop è possibile fare riferimento solo all'interno di quel loop.

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Passanti con chiusure

Se usi let invece di var in un ciclo, ad ogni iterazione ottieni una nuova variabile.Ciò significa che puoi tranquillamente utilizzare una chiusura all'interno di un ciclo.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Zona morta temporale

Per colpa di la zona morta temporale, variabili dichiarate utilizzando let non è possibile accedervi prima che vengano dichiarati.Il tentativo di farlo genera un errore.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

Nessuna nuova dichiarazione

Non è possibile dichiarare la stessa variabile più volte utilizzando let.Inoltre, non puoi dichiarare una variabile usando let con lo stesso identificatore di un'altra variabile dichiarata utilizzando var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

const è abbastanza simile a let- ha un ambito di blocco e ha TDZ.Ci sono però due cose diverse.

Nessuna riassegnazione

Variabile dichiarata utilizzando const non può essere riassegnato.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Tieni presente che ciò non significa che il valore sia immutabile.Le sue proprietà possono ancora essere modificate.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

Se vuoi avere un oggetto immutabile, dovresti usare Object.freeze().

L'inizializzatore è obbligatorio

È sempre necessario specificare un valore quando si dichiara una variabile utilizzando const.

const a; // SyntaxError: Missing initializer in const declaration

Ecco un esempio per la differenza tra i due (supporto appena iniziato per Chrome):
entrare descrizione dell'immagine qui

Come si può vedere la variabile var j continua ad avere un valore al di fuori del campo di applicazione per il ciclo (Block Ambito di applicazione), ma la variabile let i non è definito al di fuori del campo di applicazione per il ciclo.

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);

Ci sono alcune differenze sottili - let scoping si comporta più come scoping variabile fa in più o meno qualsiasi altra lingua.

es. E 'ambiti al blocco racchiude, Essi non esistono prima di essere dichiarate, ecc.

Tuttavia vale la pena notare che let è solo una parte del più recenti implementazioni Javascript e ha diversi gradi di .

  • variabile non Sollevamento

    let sarà non issare per l'intero ambito del blocco sono visualizzati. Per contro, var potrebbe issare come sotto.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    In realtà, Per @Bergi, Entrambi var e let vengono issate .

  • Garbage Collection

    ambito blocco di let è utile riferisce a chiusure e garbage collection per recuperare la memoria. Si consideri,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    Il callback gestore click non ha bisogno della variabile hugeData a tutti. Teoricamente, dopo corse process(..), l'enorme struttura di dati hugeData potrebbe essere garbage collection. Tuttavia, è possibile che qualche motore JS dovrà comunque mantenere questa enorme struttura, dal momento che la funzione click ha una chiusura sopra l'intero ambito.

    Tuttavia, la portata di blocco può fare questo enorme struttura dati a garbage collection.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
  • loop let

    let nel loop può ri-associa per ciascuna iterazione del ciclo, avendo cura di ri-assegnare il valore dalla fine del precedente iterazione del ciclo. Si consideri,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Tuttavia, sostituire var con let

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Poiché let creare un nuovo ambiente lessicale con i nomi di a) l'espressione initialiser b) ciascuna iterazione (previosly per valutare l'espressione di incremento), maggiori dettagli sono qui .

La differenza principale è la scopo differenza, mentre permettere può essere disponibile solo all'interno del scopo è dichiarato, come nel ciclo for, var è possibile accedere all'esterno del loop, ad esempio.Dalla documentazione in MDN (esempi anche da MDN):

permettere consente di dichiarare variabili con ambito limitato al blocco, all'istruzione o all'espressione in cui viene utilizzato.Questo è diverso da var parola chiave, che definisce una variabile a livello globale o localmente su un'intera funzione indipendentemente dall'ambito del blocco.

Variabili dichiarate da permettere hanno come ambito il blocco in cui sono definiti, nonché gli eventuali sottoblocchi contenuti.In questo modo, permettere funziona in modo molto simile var.La differenza principale è che l'ambito di a var variabile è l'intera funzione di inclusione:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`

Al livello più alto di programmi e funzioni, permettere, a differenza di var, non crea una proprietà sull'oggetto globale.Per esempio:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

Se utilizzato all'interno di un blocco, consente di limitare l'ambito della variabile a quel blocco.Nota la differenza tra var il cui scope è all'interno della funzione in cui è dichiarato.

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

Inoltre, non dimenticare che è la funzionalità ECMA6, quindi non è ancora completamente supportata, quindi è meglio trasporla sempre in ECMA5 usando Babel ecc...per maggiori informazioni sulla visita sito web di Babele

Ecco un esempio da aggiungere a ciò che altri hanno già scritto. Si supponga di voler fare una serie di funzioni, adderFunctions, dove ogni funzione prende un singolo argomento Numero e restituisce la somma dell'argomento e l'indice della funzione nella matrice. Cercando di generare adderFunctions con un ciclo utilizzando la parola chiave var non funziona il modo in cui qualcuno potrebbe ingenuamente aspettare:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

Il processo di cui sopra non genera la matrice desiderata di funzioni perché portata di i estende oltre l'iterazione del blocco for in cui è stato creato ogni funzione. Invece, alla fine del ciclo, il i in chiusura di ciascuna funzione si riferisce al valore di i alla fine del ciclo (1000) per ogni funzione anonima in adderFunctions. Questo non è quello che volevamo affatto: ora abbiamo una serie di 1000 diverse funzioni in memoria con esattamente lo stesso comportamento. E se successivamente aggiornare il valore della i, la mutazione interesserà tutto il adderFunctions.

Tuttavia, siamo in grado di provare di nuovo usando la parola chiave let:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

Questa volta, è i rimbalzo su ogni iterazione del ciclo for. Ogni funzione oggi mantiene il valore della i al momento della creazione della funzione, e adderFunctions si comporta come previsto.

Ora, immagine miscelazione dei due comportamenti e probabilmente vedrete il motivo per cui non è consigliabile mescolare la più recente let e const con il var vecchio nello stesso script. In questo modo può provocare è un codice spettacolare confusione.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

Non lasciate che questo accada a voi. Utilizzare un Linter.

  

Nota: Questo è un esempio di insegnamento destinato a dimostrare il comportamento var / let in loop e con chiusure di funzione che sarebbe anche facile da capire. Questo sarebbe un modo terribile per aggiungere i numeri. Ma la tecnica generale di catturare dati in chiusure di funzione anonima potrebbe essere incontrato nel mondo reale in altri contesti. YMMV.

La differenza sta nel scopo delle variabili dichiarate con ciascuno.

In pratica, ci sono una serie di conseguenze utili derivanti dalla differenza di portata:

  1. let le variabili sono visibili solo nei loro file recinto più vicino blocco ({ ... }).
  2. let le variabili sono utilizzabili solo nelle righe di codice che si verificano Dopo la variabile è dichiarata (anche se vengono issati!).
  3. let le variabili non possono essere ridichiarate da una variabile successiva var O let.
  4. Globale let le variabili non vengono aggiunte al globale window oggetto.
  5. let le variabili sono facile da usare con chiusure (non causano condizioni di gara).

Le restrizioni imposte dall let ridurre la visibilità delle variabili e aumentare la probabilità che vengano rilevate tempestivamente collisioni di nomi inaspettate.Ciò rende più semplice tenere traccia e ragionare sulle variabili, comprese le loro raggiungibilità(aiutando a recuperare la memoria inutilizzata).

Di conseguenza, let è meno probabile che le variabili causino problemi se utilizzate in programmi di grandi dimensioni o quando framework sviluppati in modo indipendente vengono combinati in modi nuovi e inaspettati.

var può ancora essere utile se sei sicuro di volere l'effetto di associazione singola quando usi una chiusura in un ciclo (#5) o per dichiarare variabili globali visibili esternamente nel tuo codice (#4).Utilizzo di var per le esportazioni possono essere sostituiti se export migra dallo spazio del transpiler al linguaggio principale.

Esempi

1.Nessun utilizzo al di fuori del blocco di recinzione più vicino:Questo blocco di codice genererà un errore di riferimento a causa del secondo utilizzo di x si verifica all'esterno del blocco con cui è dichiarato let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

Al contrario, lo stesso esempio con var lavori.

2.Nessun utilizzo prima della dichiarazione:
Questo blocco di codice genererà un file ReferenceError prima che il codice possa essere eseguito perché x viene utilizzato prima di essere dichiarato:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

Al contrario, lo stesso esempio con var analizza ed esegue senza generare eccezioni.

3.Nessuna nuova dichiarazione:Il codice seguente dimostra che una variabile dichiarata con let non possono essere ridichiarati successivamente:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4.Globali non collegati a window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5.Facile utilizzo con chiusure:Variabili dichiarate con var non funzionano bene con le chiusure all'interno dei loop.Ecco un semplice ciclo che restituisce la sequenza di valori della variabile i ha in diversi momenti:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

Nello specifico, questo restituisce:

i is 0
i is 1
i is 2
i is 3
i is 4

In JavaScript spesso utilizziamo le variabili in un momento significativamente successivo rispetto a quando vengono create.Quando lo dimostriamo ritardando l'output con una chiusura passata a setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...l'output rimane invariato finché rimaniamo fedeli let.Al contrario, se avessimo usato var i Invece:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...il ciclo restituisce inaspettatamente "i is 5" cinque volte:

i is 5
i is 5
i is 5
i is 5
i is 5

Possano le seguenti due funzioni mostrare la differenza:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}

let è interessante, perché ci permette di fare qualcosa di simile a questo:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Il che si traduce in conteggio [0, 7].

considerando che

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Solo conta [0, 1].

Funzione VS portata blocco:

La differenza principale tra var e let è che le variabili dichiarate con var sono Funzione ambito . Mentre le funzioni dichiarate con let sono blocco ambito . Ad esempio:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

variabili con var:

Quando la prima funzione testVar viene chiamata variabile foo, dichiarata con var, è ancora accessibile al di fuori della dichiarazione if. Questo foo variabile sarà disponibile in tutto il mondo nel campo di applicazione del testVar Funzione .

variabili con let:

Quando la seconda funzione testLet viene chiamata la barra variabile dichiarata con let, è accessibile solo all'interno della dichiarazione if. Perché le variabili dichiarate con let sono blocco ambito (in cui un blocco è il codice tra parentesi graffe per esempio if{}, for{}, function{}).

variabili let non vengono issate:

Un'altra differenza tra var e let è variabili con dichiarata con let non si ottiene issato . Un esempio è il modo migliore per illustrare questo comportamento:

variabili con let non vengono issati:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

variabili con var do issato:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

let globale non viene attaccato al window:

Una variabile dichiarata con let nell'ambito globale (che è il codice che non è in funzione) non ottiene aggiunto come una proprietà sull'oggetto window globale. Per esempio (questo codice è di portata globale):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object

  

Quando dovrebbe essere utilizzato più let var?

Usa let oltre var ogni volta che è possibile, perché è semplicemente ambito più specifico. Questo riduce i potenziali conflitti di nomi che possono verificarsi quando si tratta di un gran numero di variabili. var può essere utilizzato quando si desidera una variabile globale in modo esplicito di essere l'oggetto window (sempre considerare attentamente se questo è davvero necessario).

Risulta inoltre che, almeno in Visual Studio 2015, dattiloscritto 1.5, "var" consente a più dichiarazioni dello stesso nome di variabile in un blocco, e "lasciare che" non lo fa.

Questa non genererà un errore di compilazione:

var x = 1;
var x = 2;

Questa volontà:

let x = 1;
let x = 2;

var è portata variabile globale (paranco-in grado).

let e const è bloccare portata.

  

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined

quando si utilizza let

La parola chiave let attribuisce la dichiarazione di variabile per la portata di qualsiasi blocco (comunemente un paio { .. }) è contenuta in. In altre parole, let dirotta implicitamente portata di qualsiasi blocco per la sua dichiarazione delle variabili.

variabili let non è possibile accedere nell'oggetto window perché non possono accedere globalmente.

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

quando si utilizza var

var e le variabili in ES5 ha portate nelle funzioni che significano le variabili sono valide all'interno della funzione e non al di fuori della funzione stessa.

variabili var possibile accedere nell'oggetto window perché non possono accedere globalmente.

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

Se volete saperne di più continua a leggere qui di seguito

una delle più famose domande di intervista sulla portata anche può bastare il corretto utilizzo di let e var come sotto;

Quando si utilizza let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

Questo perché quando si utilizza let, per ogni iterazione del ciclo la variabile è ambito e ha la sua copia.

Quando si utilizza var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

Questo perché quando si utilizza var, per ogni iterazione del ciclo la variabile è ambito e ha condiviso la copia.

Se leggo bene le specifiche in quel momento let per fortuna può anche essere sfruttato per evitare funzioni di autoinvocazione utilizzato per simulare solo membri privati ​​- un modello di progettazione popolare che riduce la leggibilità del codice, complica il debug, che non aggiunge alcuna protezione reale del codice o altri vantaggi, tranne forse soddisfare il desiderio di semantica di qualcuno, quindi smetti di usarlo./scatenarsi

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

Vedere 'Emulazione di interfacce private'

Alcuni hack con let:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

getter e setter con let:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)

let vs var.È tutta una questione di scopo.

le variabili var sono globali ed è accessibile praticamente ovunque, mentre lascia che le variabili non siano globali ed esistono solo finché una parentesi di chiusura non li uccide.

Guarda il mio esempio qui sotto e nota come la variabile lion (let) agisce diversamente nei due console.logs;diventa fuori ambito nel 2° console.log.

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

let è una parte di ES6. Queste funzioni spiegare la differenza in modo semplice.

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

In precedenza c'erano solo due ambiti in JavaScript, vale a dire funzionali e globali. Con la parola chiave 'let' JavaScript ha introdotto variabili block-level.

Per avere una completa comprensione del 'lasciare' parola chiave, ES6: ‘lasciare’ parola chiave per dichiarare variabili in JavaScript vi aiuterà

.

Ora penso che ci sia una migliore scoping di variabili da un blocco di istruzioni che utilizzano let:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

Credo che le persone inizieranno a utilizzare lasciare qui dopo in modo che essi avranno scoping simile in JavaScript, come altre lingue, Java, C #, ecc.

Le persone con non una chiara comprensione di scoping in JavaScript utilizzati per fare l'errore precedente.

sollevamento non è supportata utilizzando let.

Con questo approccio gli errori presenti in JavaScript sono sempre rimossi.

Fare riferimento a ES6 Approfondimenti :. lasciò e const per capire meglio

Questo articolo definisce chiaramente la differenza tra var, lasciare e const

  

const è un segnale che l'identificatore non verrà riassegnato.

     

let, è un segnale che la variabile venga rimessa, quale un   contatore in un loop, o uno scambio valore in un algoritmo. Segnala inoltre   che la variabile sarà usato solo nel blocco è definito,   che non è sempre l'intera funzione di contenimento.

     

var ora è il segnale più debole disponibile quando si definisce una variabile   in JavaScript. La variabile può o non può essere riassegnato, e   variabile può o non può essere utilizzato per una funzione intera, o solo per   lo scopo di un blocco o ad anello.

https: // media. com / javascript-scena / javascript-ES6-var-let-o-const-ba58b8dcde75 # .esmkpbg9b

Come accennato in precedenza:

  

La differenza è dell'ambito. var è ambito per il funzione più vicino   blocco e let ambito la blocco racchiude vicina , che   può essere più piccolo di un blocco funzionale. Entrambi sono globali se al di fuori qualsiasi   block.Lets vedere un esempio:

Esempio 1:

Nel mio entrambi gli esempi Ho una funzione myfunc. myfunc contiene una myvar variabile è uguale a 10. Nel mio primo esempio a verificare se myvar uguale a 10 (myvar==10). Se sì, mi dichiaro agian un myvar variabile (ora ho due variabili myvar) utilizzando var parola chiave e assegnarle un nuovo valore (20). Nella riga successiva si stampa il suo valore sulla mia console. Dopo il blocco condizionale ho di nuovo stampare il valore di myvar sulla mia console. Se si guarda l'output di myfunc, myvar ha un valore uguale a 20.

lasciare parola chiave

Esempio 2: Nel mio secondo esempio invece di utilizzare var parola chiave nel mio blocco condizionale dichiaro myvar usando let parola chiave. Ora, quando chiamo myfunc ottengo due uscite diverse: myvar=20 e myvar=10

.

Quindi, la differenza è molto semplice cioè la sua portata.

entrare descrizione dell'immagine qui

Date un'occhiata a questa immagine, ho creato un esempio molto semplice per la dimostrazione delle variabili const e let. Come si può vedere, quando si tenta di modificare variabile const, si otterrà l'errore ( Il tentativo di sostituire 'nome', che è costante '), ma dare un'occhiata a variabile let ...

Per prima cosa dichiariamo let age = 33, e successivamente assegniamo qualche altro age = 34; valore, che è ok, noi non hanno eventuali errori quando si tenta di cambiare let variabile

Credo che i termini e la maggior parte degli esempi sono un po 'opprimente, Il problema principale che ho avuto personalmente con la differenza è capire cosa sia un "blocco" è. Ad un certo punto mi sono reso conto, un blocco sarebbe alcun parentesi graffe ad eccezione di dichiarazione IF. un { parentesi di apertura di una funzione o loop definire un nuovo blocco, nulla definita con let all'interno di esso, non sarà disponibile dopo il } parentesi di chiusura della stessa cosa (funzione o loop); Con questo in mente, era più facile da capire:

let msg = "Hello World";

function doWork() { // msg will be available since it was defined above this opening bracket!
  let friends = 0;
  console.log(msg);

  // with VAR though:
  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
  console.log(iCount2);
  
    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
  console.log(iCount1);
  
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);

Poiché attualmente sto cercando di comprendere a fondo JavaScript, condividerò la mia breve ricerca che contiene alcuni degli ottimi pezzi già discussi più alcuni altri dettagli in una prospettiva diversa.

Comprendere la differenza tra var E permettere può essere più semplice se comprendiamo la differenza tra funzione E ambito del blocco.

Consideriamo i seguenti casi:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same value for each call
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

Quando timer() viene chiamato an Contesto di esecuzione viene creato che conterrà sia il file Ambiente variabile e tutto il Ambienti lessicali corrispondente ad ogni iterazione.

E un esempio più semplice

Ambito della funzione

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

Ambito di blocco

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}

Voglio collegare queste parole chiave per il contesto di esecuzione, perché il contesto di esecuzione è importante in tutto questo. Il contesto di esecuzione ha due fasi: una fase di creazione e di fase di esecuzione. Inoltre, ogni contesto di esecuzione è una variabile di ambiente ed esterno Ambiente (il suo lessicale Ambiente).

Durante la fase di creazione di un contesto di esecuzione, var, lascia e const sarà ancora conservare la sua variabile nella memoria con un valore non definito nella variabile d'ambiente di un dato contesto di esecuzione. La differenza è nella fase di esecuzione. Se si utilizza una variabile di riferimento definito con var prima che venga assegnato un valore, sarà solo indefinito. Nessuna eccezione sarà sollevata.

Tuttavia, non è possibile fare riferimento alla variabile dichiarata con let o const fino a quando non viene dichiarato. Se si tenta di utilizzarlo prima che venga dichiarata, quindi un'eccezione sarà sollevata durante la fase di esecuzione del contesto di esecuzione. Ora la variabile sarà ancora in memoria, per gentile concessione della fase di creazione del contesto di esecuzione, ma il motore non permetterà di usarlo:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

Con una variabile definita con var, se il motore non riesce a trovare la variabile nella variabile d'ambiente del contesto di esecuzione corrente, quindi andrà a monte della catena portata (l'ambiente esterno) e controllare variabile d'ambiente del Outer ambiente per la variabile. Se non riesce a trovare lì, continuerà a cercare la catena Scope. Questo non è il caso di lasciare e const.

La seconda caratteristica di let è introduce ambito blocco. Blocchi sono definiti da parentesi graffe. Esempi includono blocchi funzionali, se i blocchi, per i blocchi, ecc Quando si dichiara una variabile con let all'interno di un blocco, la variabile è disponibile solo all'interno del blocco. Infatti, ogni volta che il blocco viene eseguito, ad esempio all'interno di un ciclo, si creerà una nuova variabile nella memoria.

ES6 introduce anche la parola chiave const per la dichiarazione di variabili. const è anche bloccare ambito. La differenza tra let e const è che le variabili const devono essere dichiarate utilizzando un inizializzatore, oppure genereranno un errore.

E, infine, quando si tratta del contesto di esecuzione, le variabili definite con var saranno allegate al 'questo' oggetto. Nel contesto di esecuzione globale, che sarà l'oggetto window nei browser. Questo non è il caso per let o const.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top