Jaka jest różnica między używaniem „let” i „var”?

4537

ECMAScript 6 wprowadził to letoświadczenie .

Słyszałem, że jest opisywana jako zmienna „lokalna”, ale wciąż nie jestem pewien, jak zachowuje się inaczej niż varsłowo kluczowe.

Jakie są różnice? Kiedy należy letzużyć var?

TM.
źródło
104
ECMAScript jest standardem i letjest zawarty w wersji 6 edycji i najprawdopodobniej będzie w ostatecznej specyfikacji.
Richard Ayotte
5
Zobacz kangax.github.io/es5-compat-table/es6, aby uzyskać aktualną matrycę wsparcia funkcji ES6 (w tym let). W chwili pisania tego tekstu Firefox, Chrome i IE11 obsługują go (chociaż uważam, że implementacja FF nie jest dość standardowa).
Nico Burns
22
Przez najdłuższy czas nie wiedziałem, że zakresy w pętli for są ograniczone do funkcji, w której były owinięte. Pamiętam, jak wymyśliłem to po raz pierwszy i pomyślałem, że to bardzo głupie. Widzę trochę mocy, choć teraz wiem, jak można je wykorzystać z różnych powodów i jak w niektórych przypadkach możesz chcieć użyć var ​​w pętli for i nie mieć zakresu do bloku.
Eric Bishard,
1
To jest bardzo dobra lektura wesbos.com/javascript-scoping
onmyway133
1
Dobrze wyjaśniona odpowiedź tutaj stackoverflow.com/a/43994458/5043867
Pardeep Jain

Odpowiedzi:

6095

Zasady określania zakresu

Główną różnicą są zasady określania zakresu. Zmienne zadeklarowane przez varsłowo kluczowe mają zasięg do bezpośredniego ciała funkcji (stąd zakres funkcji), a letzmienne są do bezpośredniego otaczającego bloku oznaczonego przez { }(stąd zakres bloku).

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

  console.log(foo, bar);

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

  console.log(baz); // ReferenceError
}

run();

Powodem letwprowadzenia słowa kluczowego do języka był zakres funkcji, który jest mylący i był jednym z głównych źródeł błędów w JavaScript.

Spójrz na ten przykład z innego pytania dotyczącego przepełnienia stosu :

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: 3był wysyłany do konsoli za każdym razem, gdy funcs[j]();wywoływano, ponieważ anonimowe funkcje były powiązane z tą samą zmienną.

Ludzie musieli tworzyć natychmiast wywoływane funkcje, aby uchwycić prawidłową wartość z pętli, ale było to również owłosione.

Podnoszenie

Podczas gdy zmienne zadeklarowane za pomocą varsłowa kluczowego są podnoszone (inicjowane undefinedprzed uruchomieniem kodu), co oznacza, że ​​są dostępne w swoim zakresie obejmującym nawet przed zadeklarowaniem:

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

run();

letzmienne nie są inicjowane, dopóki ich definicja nie zostanie oceniona. Dostępu do nich przed otrzymaniem wyników inicjujących w A ReferenceError. Zmienna mówi się, że znajduje się w „czasowej martwej strefie” od początku bloku do momentu przetworzenia inicjalizacji.

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

checkHoisting();

Tworzenie globalnej właściwości obiektu

Na najwyższym poziomie, letw przeciwieństwie do var, nie tworzy właściwości na obiekcie globalnym:

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

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

Deklaracja

W trybie ścisłym varpozwoli Ci ponownie zadeklarować tę samą zmienną w tym samym zakresie, jednocześnie letpodnosząc błąd 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
ThinkingStiff
źródło
22
Pamiętaj, że możesz stworzyć blok kiedy tylko chcesz. function () {code; {let inBlock = 5; } kod; };
średni Joe
177
Czy zatem celem instrukcji let jest jedynie zwolnienie pamięci, gdy nie jest potrzebna w określonym bloku?
NoBugs,
219
@NoBugs, Tak, i zaleca się, aby zmienne istniały tylko tam, gdzie są potrzebne.
batman
67
letwyrażenie blokowe let (variable declaration) statementjest niestandardowe i zostanie w przyszłości usunięte, bugzilla.mozilla.org/show_bug.cgi?id=1023609 .
Gajus,
19
Po prostu nie mogę wymyślić żadnego przypadku, w którym użycie var jest przydatne. Czy ktoś mógłby mi podać przykład sytuacji, w której lepiej jest używać var?
Luis Sieira,
621

letmożna również użyć, aby uniknąć problemów z zamknięciami. Wiąże świeżą wartość zamiast utrzymywać stare odniesienie, jak pokazano w poniższych przykładach.

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>
<p>Clicking on each number will log to console:</p> 
<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>

Powyższy kod pokazuje klasyczny problem z zamykaniem JavaScript. Odwołanie do izmiennej jest przechowywane w zamknięciu modułu obsługi kliknięć zamiast rzeczywistej wartości i.

Każda procedura obsługi pojedynczego kliknięcia będzie odnosić się do tego samego obiektu, ponieważ jest tylko jeden obiekt licznika, który ma 6, więc dostajesz sześć za każdym kliknięciem.

Ogólnym obejściem jest zawinięcie tego w anonimową funkcję i przekazanie jej ijako argumentu. Takich problemów można teraz również uniknąć, używając letzamiast tego, varjak pokazano w poniższym kodzie.

(Testowane w Chrome i 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>
<p>Clicking on each number will log to console:</p> 
<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>

Gurpreet Singh
źródło
54
To jest naprawdę fajne. Spodziewałbym się, że „i” zostanie zdefiniowane poza ciałem pętli zawierającym się w nawiasach i NIE będzie tworzyć „zamknięcia” wokół „i”. Oczywiście twój przykład pokazuje inaczej. Myślę, że jest to nieco mylące z punktu widzenia składni, ale ten scenariusz jest tak powszechny, że warto go w ten sposób wspierać. Wielkie dzięki za poruszenie tego tematu.
Karol Kolenda
9
IE 11 obsługuje let, ale ostrzega „6” dla wszystkich przycisków. Czy masz jakieś źródło, które mówi, jak letma się zachowywać?
Jim Hunziker,
10
Wygląda na to, że twoją odpowiedzią jest prawidłowe zachowanie: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jim Hunziker
11
Rzeczywiście jest to częsta pułapka w Javascripcie i teraz widzę, dlaczego letbyłby tak przydatny. Ustawienie detektorów zdarzeń w pętli nie wymaga już bezpośrednio wywoływanego wyrażenia funkcji do lokalnego określania zakresu iprzy każdej iteracji.
Adrian Moisa
19
Użycie „let” tylko opóźnia ten problem. Tak więc każda iteracja tworzy prywatny niezależny zakres bloku, ale zmienna „i” może być nadal uszkodzona przez kolejne zmiany w bloku (oczywiście jeśli zmienna iteratora nie jest zwykle zmieniana w bloku, ale inne zadeklarowane zmienne let w bloku mogą równie dobrze być) i każda funkcja zadeklarowana wewnątrz może zablokować, gdy wywoływany, uszkodzony wartość „i” na inne funkcje zadeklarowane w bloku, ponieważ zrobić podziela tą prywatną zakresu bloku stąd takie same odniesienia do „ja”.
gary
198

Jaka jest różnica między leti var?

  • Zmienna zdefiniowana za pomocą varinstrukcji jest znana w całej funkcji, w której jest zdefiniowana, od początku funkcji. (*)
  • Zmienna zdefiniowana za pomocą letinstrukcji jest znana tylko w bloku, w którym jest zdefiniowana, od momentu jej zdefiniowania. (**)

Aby zrozumieć różnicę, rozważ następujący kod:

// 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

Tutaj możemy zobaczyć, że nasza zmienna jjest znana tylko w pierwszej pętli for, ale nie przed i po. Jednak nasza zmienna ijest znana w całej funkcji.

Należy również wziąć pod uwagę, że zmienne o zasięgu blokowym nie są znane przed zadeklarowaniem, ponieważ nie są podnoszone. Nie można również ponownie ustawić tej samej zmiennej o zasięgu blokowym w tym samym bloku. To sprawia, że ​​zmienne o zasięgu blokowym są mniej podatne na błędy niż zmienne o zasięgu globalnym lub funkcjonalnym, które są podnoszone i które nie powodują żadnych błędów w przypadku wielu deklaracji.


Czy korzystanie z niego jest letdziś bezpieczne ?

Niektórzy twierdzą, że w przyszłości będziemy używać TYLKO instrukcji let, a instrukcje var staną się nieaktualne. Kyle Simpson, guru JavaScript, napisał bardzo skomplikowany artykuł o tym, dlaczego uważa, że ​​tak nie będzie .

Jednak dzisiaj zdecydowanie tak nie jest. W rzeczywistości musimy zadać sobie pytanie, czy korzystanie z tego letoświadczenia jest bezpieczne . Odpowiedź na to pytanie zależy od środowiska:

  • Jeśli piszesz kod JavaScript po stronie serwera ( Node.js ), możesz bezpiecznie użyć letinstrukcji.

  • Jeśli piszesz kod JavaScript po stronie klienta i używasz transpilatora opartego na przeglądarce (takiego jak Traceur lub Babel-standalone ), możesz bezpiecznie użyć tego letoświadczenia, jednak Twój kod prawdopodobnie nie będzie optymalny pod względem wydajności.

  • Jeśli piszesz kod JavaScript po stronie klienta i używasz transpilatora opartego na Węźle (takiego jak skrypt powłoki traceur lub Babel ), możesz bezpiecznie użyć letinstrukcji. Ponieważ Twoja przeglądarka będzie wiedziała tylko o transpilowanym kodzie, wady wydajności powinny być ograniczone.

  • Jeśli piszesz kod JavaScript po stronie klienta i nie używasz transpilatora, musisz rozważyć obsługę przeglądarki.

    Nadal istnieją niektóre przeglądarki, które w ogóle nie obsługują let:

wprowadź opis zdjęcia tutaj


Jak śledzić obsługę przeglądarki

Aby letzapoznać się z aktualnym przeglądem przeglądarek obsługujących to oświadczenie podczas czytania tej odpowiedzi, zobacz Can I Usestronę .


(*) Zmienne o zasięgu globalnym i funkcjonalnym mogą być inicjowane i używane przed zadeklarowaniem, ponieważ zmienne JavaScript są podnoszone . Oznacza to, że deklaracje są zawsze na szczycie zakresu.

(**) Zmienne o zasięgu blokowym nie są podnoszone

John Slegers
źródło
14
odnośnie odpowiedzi v4: iJEST znany wszędzie w bloku funkcyjnym! Zaczyna się jako undefined(z powodu podnoszenia) do momentu przypisania wartości! ps: letjest również podnoszony (na górę, w której znajduje się blok), ale da znak, ReferenceErrorgdy będzie się do niego odwoływał w bloku przed pierwszym przypisaniem. (ps2: Jestem facetem trochę za średnikiem, ale tak naprawdę nie potrzebujesz średnika po bloku). To powiedziawszy, dziękuję za dodanie kontroli rzeczywistości dotyczącej wsparcia!
GitaarLAB
@GitaarLAB: Według Mozilla Developer Network : „W ECMAScript 2015 niech powiązania nie podlegają podnoszeniu zmiennych, co oznacza, że ​​deklaracje nie mogą być przenoszone na szczyt bieżącego kontekstu wykonania”. - W każdym razie wprowadziłem kilka poprawek do mojej odpowiedzi, które powinny wyjaśnić różnicę w zachowaniu podnoszenia pomiędzy leti var!
John Slegers
1
Twoja odpowiedź bardzo się poprawiła (dokładnie sprawdziłem). Zauważ, że ten sam link, do którego odwołujesz się w komentarzu, mówi również: „Zmienna (let) znajduje się w„ czasowej martwej strefie ”od początku bloku do momentu przetworzenia inicjalizacji.” Oznacza to, że „identyfikator” (ciąg tekstowy „zarezerwowany”, aby wskazać „coś”) jest już zarezerwowany w odpowiednim zakresie, w przeciwnym razie stałby się częścią zakresu głównego / hosta / okna. Dla mnie osobiście „podnoszenie” oznacza jedynie rezerwację / powiązanie zadeklarowanych „identyfikatorów” z ich odpowiednim zakresem; z wyłączeniem ich inicjalizacji / przypisania / możliwości modyfikacji!
GitaarLAB
I .. + 1. Ten artykuł Kyle Simpson, który podłączyłeś, jest świetną lekturą, dziękuję za to! Wyraźnie widać także „czasową martwą strefę”, czyli „TDZ”. Interesującą rzeczą chciałbym dodać: Czytałem na MDN że leti constzostały zalecane do użytku, kiedy rzeczywiście potrzebują dodatkowej funkcjonalności , gdyż egzekwowanie / sprawdzanie tych dodatkowych funkcji (jak tylko do zapisu const) wynik w „więcej pracy „(i dodatkowe węzły zakresu w drzewie zakresu) dla (aktualnych) silników w celu wymuszenia / sprawdzenia / weryfikacji / konfiguracji.
GitaarLAB
1
Zauważ, że MDN mówi, że IE NIE interpretuje let poprawnie. Który to jest? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Katinka Hesselink
146

Oto wyjaśnienie letsłowa kluczowego z kilkoma przykładami.

letdziała bardzo podobnie var. Główną różnicą jest to, że zakresem varzmiennej jest cała funkcja zamykająca

Ta tabela na Wikipedii pokazuje, które przeglądarki obsługują Javascript 1.7.

Pamiętaj, że obsługują go tylko przeglądarki Mozilla i Chrome. IE, Safari i potencjalnie inni nie.

Ben S.
źródło
5
Kluczowym fragmentem tekstu z połączonego dokumentu wydaje się być: „niech działa bardzo podobnie do var. Główną różnicą jest to, że zakres zmiennej var jest całą funkcją zamykającą”.
Michael Burr
50
Chociaż technicznie poprawne jest stwierdzenie, że IE go nie obsługuje, bardziej poprawne jest stwierdzenie, że jest to rozszerzenie tylko dla Mozilli.
olliej
55
@olliej, właściwie Mozilla jest tuż przed grą. Zobacz strona 19 ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
Tyler Crompton
@TylerCrompton to tylko zestaw słów, które zostały zarezerwowane na lata. Kiedy dodana mozilla, niech będzie czysto rozszerzeniem mozilli, bez powiązanej specyfikacji. ES6 powinien zdefiniować zachowanie instrukcji let, ale pojawiło się to po wprowadzeniu przez Mozillę składni. Pamiętaj, że moz ma także E4X, który jest całkowicie martwy i tylko moz.
olliej
112

W zaakceptowanej odpowiedzi brakuje punktu:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
Lcf.vs
źródło
19
Przyjęta odpowiedź NIE wyjaśnia tego punktu w swoim przykładzie. Przyjęta odpowiedź wykazała to tylko w forinicjalizatorze pętli, co radykalnie zawęziło zakres zastosowania ograniczeń let. Pozytywne.
Jon Davis,
37
@ stimpy77 Wyraźnie stwierdza: „zakres jest ograniczony do najbliższego otaczającego bloku”; Czy należy uwzględnić każdy przejaw manifestacji?
Dave Newton
6
było wiele przykładów i żaden z nich właściwie nie wykazał sprawy. Mogłem poprzeć zarówno zaakceptowaną odpowiedź, jak i tę?
Jon Davis,
5
Ten wkład pokazuje, że „blok” może być po prostu zbiorem linii ujętych w nawiasy; tzn. nie musi być związany z jakimkolwiek przepływem kontrolnym, pętlą itp.
webelo,
81

let

Zablokuj zakres

Zmienne zadeklarowane przy użyciu letsłowa kluczowego mają zasięg blokowy, co oznacza, że ​​są dostępne tylko w bloku w którym zostały zadeklarowane.

Na najwyższym poziomie (poza funkcją)

Na najwyższym poziomie zmienne zadeklarowane za pomocą letnie tworzą właściwości obiektu globalnego.

var globalVariable = 42;
let blockScopedVariable = 43;

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

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

Wewnątrz funkcji

Wewnątrz funkcji (ale poza blokiem) letma taki sam zasięg jak 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

Wewnątrz bloku

Do zmiennych zadeklarowanych za pomocą letwewnątrz bloku nie można uzyskać dostępu poza tym blokiem.

{
  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

Wewnątrz pętli

Do zmiennych zadeklarowanych letw pętlach można odwoływać się tylko wewnątrz tej pętli.

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.

Pętle z zapięciami

Jeśli użyjesz letzamiast varw pętli, z każdą iteracją otrzymujesz nową zmienną. Oznacza to, że możesz bezpiecznie użyć zamknięcia wewnątrz pętli.

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

Czasowa martwa strefa

Ze względu na czasową martwą strefę , zmienne zadeklarowane za pomocą letnie mogą być dostępne przed zadeklarowaniem. Próba zrobienia tego powoduje błąd.

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

Brak ponownego zgłoszenia

Nie można zadeklarować tej samej zmiennej wiele razy przy użyciu let. Nie można również zadeklarować zmiennej za letpomocą tego samego identyfikatora, co inną zmienną, która została zadeklarowana za pomocą 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

constjest dość podobny do let- ma blokowy zasięg i ma TDZ. Istnieją jednak dwie rzeczy, które są różne.

Bez ponownego przypisywania

Zmiennej zadeklarowanej za pomocą constnie można ponownie przypisać.

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

Zauważ, że nie oznacza to, że wartość jest niezmienna. Jego właściwości wciąż można zmienić.

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

Jeśli chcesz mieć niezmienny obiekt, powinieneś użyć Object.freeze().

Wymagany jest inicjator

Zawsze musisz podać wartość, deklarując zmienną za pomocą const.

const a; // SyntaxError: Missing initializer in const declaration
Michał Perłakowski
źródło
51

Oto przykład różnicy między nimi (obsługa właśnie uruchomiona dla chrome):
wprowadź opis zdjęcia tutaj

Jak widać, var jzmienna nadal ma wartość poza zakresem pętli for (zakres bloku), ale let izmienna jest niezdefiniowana poza zakresem pętli for.

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

vlio20
źródło
2
Na jakie narzędzie tu patrzę?
Barton
20
Chrome devtools
vlio20
Jako twórca apletów komputerowych dla Cinnamon nie byłem narażony na tak błyszczące narzędzia.
Barton
48

Istnieją pewne subtelne różnice - let zakresy zachowują się bardziej jak zmienne zakresy w mniej więcej dowolnym innym języku.

np. obejmuje zakres otaczającego bloku, nie istnieją zanim zostaną zadeklarowane itp.

Warto jednak zauważyć, że letjest to tylko część nowszych implementacji Javascript i ma różne stopnie obsługi przeglądarki .

olliej
źródło
11
Warto również zauważyć, że ECMAScript jest standardem i letjest uwzględniony w wersji roboczej 6. edycji i najprawdopodobniej będzie w ostatecznej specyfikacji.
Richard Ayotte
23
Taką różnicę robi 3 lata: D
olliej
4
Właśnie opublikowałem to pytanie, aw 2012 roku nadal jest tak, że tylko przeglądarki Mozilla obsługują let. Safari, IE i Chome nie wszystkie.
pseudosavant
2
Pomysł przypadkowego utworzenia częściowego zakresu bloku na wypadek jest dobrym rozwiązaniem, uwaga, letnie podnosi, aby użyć zmiennej zdefiniowanej przez letdefinicję na górze bloku. Jeśli masz ifinstrukcję, która jest więcej niż tylko kilkoma wierszami kodu, możesz zapomnieć, że nie możesz użyć tej zmiennej, dopóki nie zostanie zdefiniowana. WIELKI PUNKT !!!
Eric Bishard,
2
@EricB: tak i nie: „W ECMAScript 2015, let będzie podnosić zmiennej do górnej części bloku Jednak odwołanie do zmiennej w bloku przed otrzymaniem wyników deklaracji zmiennej w. ReferenceError (moja uwaga: zamiast stary dobry undefined). Na zmienna znajduje się w „czasowej martwej strefie” od początku bloku do momentu przetworzenia deklaracji. ” To samo dotyczy „instrukcji przełączania, ponieważ istnieje tylko jeden podstawowy blok”. Źródło: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
GitaarLAB
26

Główną różnicą jest zakres różnicy, natomiast let mogą być dostępne tylko wewnątrz zakresu to zadeklarowanej, jak w pętli for var mogą być dostępne poza pętlą na przykład. Z dokumentacji w MDN (przykłady również z MDN):

let pozwala deklarować zmienne o ograniczonym zakresie do bloku, instrukcji lub wyrażenia, w którym jest używany. W przeciwieństwie do słowa kluczowego var , które definiuje zmienną globalnie lub lokalnie dla całej funkcji, niezależnie od zasięgu bloku.

Zmienne zadeklarowane przez let mają za swój zakres blok, w którym są zdefiniowane, a także we wszystkich zawartych podblokach. W ten sposób niech działa bardzo podobnie do var . Główną różnicą jest to, że zakresem zmiennej var jest cała obejmująca funkcja:

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

Na najwyższym poziomie programów i funkcji, niech , w przeciwieństwie do var , nie tworzy właściwości na obiekcie globalnym. Na przykład:

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

W przypadku użycia wewnątrz bloku, ogranicz zakres zmiennej do tego bloku. Zwróć uwagę na różnicę między var, którego zakres znajduje się w funkcji, w której został zadeklarowany.

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

Nie zapominaj także, że jest to funkcja ECMA6, więc nie jest jeszcze w pełni obsługiwana, więc lepiej zawsze przenosi ją na ECMA5 przy użyciu Babel itp. ... aby uzyskać więcej informacji na temat odwiedzenia strony Babel

Alireza
źródło
24
  • Zmienna bez podnoszenia

    letnie będzie podnosić do całego zakresu bloku, w którym się pojawiają. Przeciwnie, varmoże podnosić jak poniżej.

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

    Właściwie, Per @Bergi, Both vari letsą podnoszone .

  • Zbieranie śmieci

    Zakres bloków letjest przydatny w odniesieniu do zamknięć i odśmiecania pamięci w celu odzyskania pamięci. Rozważać,

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

    Funkcja clickzwrotna programu obsługi w ogóle nie potrzebuje hugeDatazmiennej. Teoretycznie po process(..)uruchomieniu ogromna struktura danych hugeDatamogłaby zostać wyrzucona do śmieci. Jednak możliwe jest, że niektóre silniki JS nadal będą musiały zachować tę ogromną strukturę, ponieważ clickfunkcja ma zamknięcie w całym zakresie.

    Jednak zakres bloku może sprawić, że ta ogromna struktura danych zostanie wyrzucona do śmieci.

    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){
        //....
    });
  • let pętle

    letw pętli może ponownie powiązać ją z każdą iteracją pętli, pamiętając o ponownym przypisaniu jej wartości z końca poprzedniej iteracji pętli. Rozważać,

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

    Jednakże, należy wymienić varzlet

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

    Ponieważ letstwórz nowe środowisko leksykalne o tych nazwach dla a) wyrażenia inicjującego b) każdej iteracji (poprzednio do oceny wyrażenia przyrostowego), więcej szczegółów znajduje się tutaj .

zangw
źródło
4
Yip są podnoszone, ale zachowują się, jakby nie zostały podniesione z powodu (bębna) Temporal Dead Zone - bardzo dramatyczna nazwa identyfikatora, który nie jest dostępny, dopóki nie zostanie ogłoszony :-)
Drenai
Więc jest podniesiony, ale niedostępny? Czym różni się to od „niepodnoszenia”?
N-ate
Mam nadzieję, że Brian lub Bergi wrócą, aby odpowiedzieć na to pytanie. Czy deklaracja „let” została podniesiona, ale nie przydział? Dzięki!
N-ate
1
@ N-ate, Oto jeden post Bergi, może znajdziesz w nim odpowiedź.
zangw
Ciekawe, że nawet jeśli chodzi o wynajem, nazywa się go podnoszeniem. Rozumiem, że technicznie silnik analizujący go wstępnie przechwytuje, ale programista powinien traktować go tak, jakby nie istniał. Z drugiej strony podnoszenie var ma wpływ na programistę.
N-ate
19

Oto przykład dodania do tego, co już napisali inni. Załóżmy, że chcesz utworzyć tablicę funkcji, adderFunctionsw której każda funkcja pobiera pojedynczy argument Number i zwraca sumę argumentu oraz indeks funkcji w tablicy. Próba wygenerowania adderFunctionsza pomocą pętli przy użyciu varsłowa kluczowego nie będzie działać tak, jak ktoś mógłby naiwnie oczekiwać:

// 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

Powyższy proces nie generuje pożądanej tablicy funkcji, ponieważ izakres wykracza poza iterację forbloku, w którym każda funkcja została utworzona. Zamiast tego na końcu pętli zamknięcie iw każdej funkcji odnosi się do iwartości na końcu pętli (1000) dla każdej anonimowej funkcji w adderFunctions. Nie tego w ogóle chcieliśmy: mamy teraz w pamięci tablicę 1000 różnych funkcji o dokładnie takim samym zachowaniu. A jeśli następnie zaktualizujemy wartość i, mutacja wpłynie na wszystkie adderFunctions.

Możemy jednak spróbować ponownie, używając letsłowa kluczowego:

// 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

Tym razem iodbija się przy każdej iteracji forpętli. Każda funkcja zachowuje teraz wartość iw momencie jej tworzenia, orazadderFunctions zachowuje się zgodnie z oczekiwaniami.

Teraz, obraz mieszający te dwa zachowania i prawdopodobnie zobaczysz, dlaczego nie zaleca się mieszania nowszych leti conststarszych varw tym samym skrypcie. Może to spowodować spektakularnie zagmatwany kod.

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

Nie pozwól, aby ci się to przytrafiło. Użyj wkładki.

UWAGA: To jest przykład nauczania mający na celu zademonstrowanie zachowania var/ letw pętlach i przy zamknięciach funkcji, które również byłyby łatwe do zrozumienia. Byłby to okropny sposób dodawania liczb. Ale ogólną technikę przechwytywania danych w anonimowych zamknięciach funkcji można spotkać w świecie rzeczywistym w innych kontekstach. YMMV.

abroz
źródło
2
@aborz: Również bardzo fajna składnia funkcji anonimowych w drugim przykładzie. Właśnie do tego jestem przyzwyczajony w C #. Nauczyłem się dziś czegoś.
Barton,
Korekta: Technicznie opisana tutaj składnia funkcji strzałki => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Barton
3
W rzeczywistości nie potrzebujesz let value = i;. forInstrukcja tworzy leksykalny blok.
Szczoteczka do zębów
17

Różnica polega na zakresie zmiennych zadeklarowanych z każdą z nich.

W praktyce istnieje szereg przydatnych konsekwencji różnicy w zakresie:

  1. letzmienne są widoczne tylko w ich najbliższym otaczającym bloku ( { ... }).
  2. letzmienne są użyteczne tylko w wierszach kodu, które występują po zadeklarowaniu zmiennej (nawet jeśli są podnoszone !).
  3. letzmienne nie mogą być ponownie zadeklarowane przez kolejne varlub let.
  4. letZmienne globalne nie są dodawane do windowobiektu globalnego .
  5. letzmienne są łatwe w użyciu przy zamknięciach (nie powodują warunków wyścigu ).

Ograniczenia nałożone przez letzmniejszają widoczność zmiennych i zwiększają prawdopodobieństwo wczesnego wykrycia nieoczekiwanych kolizji nazw. Ułatwia to śledzenie i uzasadnianie zmiennych, w tym ich osiągalności (pomoc w odzyskiwaniu nieużywanej pamięci).

W związku z tym letzmienne rzadziej powodują problemy, gdy są używane w dużych programach lub gdy niezależnie opracowane ramy są łączone w nowy i nieoczekiwany sposób.

varmoże być nadal przydatny, jeśli masz pewność, że chcesz efektu pojedynczego wiązania podczas zamykania w pętli (# 5) lub do deklarowania zewnętrznych widocznych zmiennych globalnych w kodzie (# 4). Użycie vardo eksportu może zostać zastąpione, jeśli exportmigruje z miejsca transpilatora do podstawowego języka.

Przykłady

1. Brak zastosowania poza najbliższym załączającym blokiem: ten blok kodu zgłosi błąd odniesienia, ponieważ drugie użycie xwystępuje poza blokiem, w którym jest zadeklarowane za pomocą let:

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

Natomiast ten sam przykład z vardziełami.

2. Nie używaj przed deklaracją:
ten blok kodu wyrzuci ReferenceErrorprzed uruchomieniem kodu, ponieważ xjest używany przed zadeklarowaniem:

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

W przeciwieństwie do tego samego przykładu z varanalizami i uruchomieniami bez zgłaszania wyjątków.

3. Brak ponownej deklaracji: Poniższy kod pokazuje, że zmienna zadeklarowana za pomocą letmoże nie zostać później ponownie zadeklarowana:

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

4. Globały nieprzyłączone do 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. Łatwość użycia z zamknięciami: Deklarowane zmienne varnie działają dobrze z zamknięciami wewnątrz pętli. Oto prosta pętla, która generuje ciąg wartości, które zmienna ima w różnych punktach czasowych:

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

W szczególności wyniki te:

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

W JavaScript często używamy zmiennych znacznie później niż w momencie ich tworzenia. Kiedy wykażemy to, opóźniając wyjście o zamknięcie przekazane do setTimeout:

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

... wynik pozostaje niezmieniony, dopóki trzymamy się let. Natomiast gdybyśmy var izamiast tego użyli :

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

... pętla nieoczekiwanie wyprowadza pięć razy „i ma 5”:

i is 5
i is 5
i is 5
i is 5
i is 5
mormegil
źródło
5
# 5 nie jest spowodowany warunkami wyścigu. Używając varzamiast let, kod jest równoważny z: var i = 0; while (i < 5) { doSomethingLater(); i++; } iznajduje się poza zamknięciem, a do czasu doSomethingLater()wykonania izostał już zwiększony 5 razy, stąd wynik jest i is 5pięć razy. Dzięki użyciu letzmienna iznajduje się w zamknięciu, więc każde wywołanie asynchroniczne otrzymuje własną kopię izamiast używać „globalnej”, która została utworzona var.
Daniel T.
@DanielT .: Nie sądzę, żeby transformacja polegająca na usunięciu definicji zmiennej z inicjatora pętli cokolwiek wyjaśniała. To jest po prostu normalna definicja semantyki for. Bardziej dokładna transformacja, choć bardziej skomplikowana, to klasyczna for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i to $ {j}, ), 125/*ms*/); })(i); }która wprowadza „rekord aktywacji funkcji”, aby zapisać każdą wartość iz nazwą jwewnątrz funkcji.
mormegil,
14

Niech następujące dwie funkcje pokazują różnicę:

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
}
Abdennour TOUMI
źródło
13

let jest interesujące, ponieważ pozwala nam zrobić coś takiego:

(() => {
    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++);
            }
        }
    }
})();

Co powoduje zliczanie [0, 7].

Natomiast

(() => {
    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++);
            }
        }
    }
})();

Liczy się tylko [0, 1].

Dmitry
źródło
2
po raz pierwszy widziałem, żeby ktokolwiek zachowywał się, jakby pożądane było cieniowanie zmiennych. nie, celem let nie jest włączenie shadowingu
John Haugeland,
1
cel, powód? jest to konstrukcja, możesz jej używać, jak chcesz, jeden z ciekawych sposobów jest taki.
Dmitry
13

Funkcja VS zakres bloku:

Główna różnica między vari letpolega na tym, że zmienne zadeklarowane za pomocą varmają zasięg działania . Natomiast funkcje zadeklarowane za pomocą letmają zasięg blokowy . Na przykład:

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 

zmienne z var:

Gdy pierwsza funkcja testVarzostanie wywołana, zmienna foo, zadeklarowana za pomocą var, jest nadal dostępna poza ifinstrukcją. Ta zmienna foobyłaby dostępna wszędzie w zakresie testVar funkcji .

zmienne z let:

Gdy druga funkcja testLetzostanie wywołana, pasek zmiennych, zadeklarowany za pomocą let, jest dostępny tylko wewnątrz ifinstrukcji. Ponieważ zmienne zadeklarowaną letbloku o zakresie (blok ma kod pomiędzy klamrami na przykład if{}, for{}, function{}).

let zmienne nie są podnoszone:

Kolejną różnicą pomiędzy vari letsą zmienne z zadeklarowanym za pomocą let nie daj się podnieść . Przykład jest najlepszym sposobem zilustrowania tego zachowania:

zmienne z let nie są podnoszone:

console.log(letVar);

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

zmienne z var do get hoisted:

console.log(varVar);

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

Globalny letnie przywiązuje się do window:

Zmienna zadeklarowana letw zakresie globalnym (który jest kodem, który nie jest funkcją) nie jest dodawana jako właściwość windowobiektu globalnego . Na przykład (ten kod ma zasięg globalny):

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


Kiedy należy letzużyć var?

Użyj letnad vardowolnym momencie może dlatego, że jest po prostu scoped bardziej szczegółowe. Zmniejsza to potencjalne konflikty nazewnictwa, które mogą wystąpić w przypadku dużej liczby zmiennych. varmoże być użyty, gdy chcesz, aby zmienna globalna była wyraźnie na windowobiekcie (zawsze dokładnie rozważ, czy jest to naprawdę konieczne).

Willem van der Veen
źródło
9

Wydaje się również, że przynajmniej w Visual Studio 2015, TypeScript 1.5, „var” dopuszcza wiele deklaracji o tej samej nazwie zmiennej w bloku, a „let” nie.

To nie wygeneruje błędu kompilacji:

var x = 1;
var x = 2;

Spowoduje to:

let x = 1;
let x = 2;
RDoc
źródło
9

var jest zmienną zasięgu globalnego (zdolną do podnoszenia).

leti constma zakres blokowy.

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

Muzułmanin Shahsavan
źródło
8

Podczas używania let

letKluczowe przywiązuje deklaracji zmiennej do zakresu jakiegokolwiek bloku (powszechnie { .. }para) to zawarty w. Innymi słowy, letw sposób dorozumiany hijacks zakres dowolny blok za jego deklaracji zmiennej.

letzmienne nie mogą być dostępne w windowobiekcie, ponieważ nie można uzyskać do nich globalnego dostępu.

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

Podczas używania var

var a zmienne w ES5 mają zakresy funkcji, co oznacza, że ​​zmienne są prawidłowe w obrębie funkcji, a nie poza samą funkcją.

varzmienne mogą być dostępne w windowobiekcie, ponieważ nie można uzyskać do nich globalnego dostępu.

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

Jeśli chcesz dowiedzieć się więcej, czytaj dalej poniżej

jedno z najbardziej znanych pytań na temat zakresu wywiadu może również wystarczyć do dokładnego użycia leti varjak poniżej;

Podczas używania let

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

Dzieje się tak, ponieważ podczas używania let, dla każdej iteracji pętli zmienna ma zakres i ma swoją własną kopię.

Podczas używania var

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

Dzieje się tak, ponieważ podczas używania var, dla każdej iteracji pętli zmienna ma zakres i ma współdzieloną kopię.

Ankur Soni
źródło
7

Jeśli dobrze przeczytam specyfikację, to na let szczęście można ją również wykorzystać, aby uniknąć wywoływania funkcji używanych do symulacji tylko prywatnych członków - popularny wzorzec projektowy, który zmniejsza czytelność kodu, komplikuje debugowanie, nie dodaje żadnej prawdziwej ochrony kodu ani innych korzyści - z wyjątkiem może zaspokojenia czyichś pragnienie semantyki, więc przestań go używać. /tyrada

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

Zobacz „ Emulowanie prywatnych interfejsów

Daniel Sokołowski
źródło
Czy potrafisz wyjaśnić, w jaki sposób Wywołane natychmiastowe funkcje nie zapewniają „ochrony kodu” let? (Zakładam, że masz na myśli IIFE z „funkcją samodzielnego wywoływania”).
Robert Siemer
A dlaczego ustawiasz hiddenPropertyw konstruktorze? Jest tylko jeden hiddenPropertydla wszystkich instancji w twojej „klasie”.
Robert Siemer
5

W najbardziej podstawowych terminach

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ Piaskownica do zabawy ↓

Edytuj let vs. var

Hasan Sefa Ozalp
źródło
4

Niektóre hacki z 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 i seter z 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)
zloctb
źródło
proszę, co to znaczy let { type, name, value } = node;? tworzysz nowy obiekt z 3 właściwościami typ / nazwa / wartość i inicjujesz je wartościami właściwości z węzła?
AlainIb
W przykładzie 3 ponownie deklarujesz węzeł, który powoduje wyjątek. Te wszystkie przykłady również działają doskonale var.
Rehan Haider
4

let vs var. Chodzi o zakres .

Zmienne var są globalne i można uzyskać do nich dostęp praktycznie wszędzie, natomiast zmienne nie są globalne i istnieją tylko do momentu, gdy nawias zamykający je zabije.

Zobacz mój przykład poniżej i zauważ, jak zmienna lion (let) działa inaczej w dwóch console.logs; staje się poza zakresem w 2. konsoli.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.
daCoda
źródło
4

ES6 wprowadził dwa nowe słowa kluczowe ( let i const ) na przemian do var .

Gdy potrzebujesz opóźnienia na poziomie bloku, możesz użyć let i const zamiast var.

Poniższa tabela podsumowuje różnicę między var, let i const

wprowadź opis zdjęcia tutaj

Srikrushna
źródło
3

let jest częścią es6. Funkcje te wyjaśnią różnicę w łatwy sposób.

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
}
VIP JUL
źródło
3

Poniżej pokazano, jak różnią się zasięgiem „let” i „var”:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

gfoo, Zdefiniowane przez letpoczątkowo znajduje się w zasięgu globalnym , a kiedy deklarujemy gfooponownie wewnątrz if clausejego zakresu zmieniło i gdy nowa wartość jest przypisana do zmiennej wewnątrz tego zakresu to nie wpływa na zasięg globalny.

Podczas gdy hfoozdefiniowany przez varjest początkowo w zakresie globalnym , ale ponownie, kiedy deklarujemy go w zakresie if clause, uwzględnia globalny zakres hfoo, chociaż var został ponownie użyty do jego zadeklarowania. Kiedy ponownie przypisujemy jego wartość, widzimy, że wpływa to również na globalny zasięg hfoo. To jest podstawowa różnica.

Piklu Dey
źródło
2

Jak wspomniano powyżej:

Różnica polega na określeniu zakresu. varma zasięg do najbliższego bloku funkcyjnego i letjest kierowany do najbliższego otaczającego bloku , który może być mniejszy niż blok funkcyjny. Oba są globalne, jeśli znajdują się poza jakimkolwiek blokiem. Zobaczmy przykład:

Przykład 1:

W obu moich przykładach mam funkcję myfunc. myfunczawiera zmienną myvarrówną 10. W moim pierwszym przykładzie sprawdzam, czy myvarrówna się 10 ( myvar==10). Jeśli tak, deklaruję zmienną myvar(teraz mam dwie zmienne myvar) za pomocą varsłowa kluczowego i przypisuję jej nową wartość (20). W następnym wierszu wypisuję jego wartość na konsoli. Po bloku warunkowym ponownie wypisuję wartość myvarna konsoli. Jeśli spojrzysz na wynik myfunc, myvarma wartość równą 20.

niech słowo kluczowe

Przykład 2: W moim drugim przykładzie zamiast użycia varsłowa kluczowego w moim bloku warunkowym deklaruję myvarużycie letsłowa kluczowego. Teraz, kiedy dzwonię myfunc , otrzymuję dwa różne wyjścia: myvar=20i myvar=10.

Różnica jest więc bardzo prosta, tzn. Jej zakres.

N Randhawa
źródło
3
Nie publikuj zdjęć kodu, jest to uważane za złą praktykę na SO, ponieważ nie będzie można go wyszukiwać dla przyszłych użytkowników (jak również problemów związanych z dostępnością). Również ta odpowiedź nie dodaje niczego, na co inne odpowiedzi jeszcze nie odpowiedziały.
inostia
2

Chcę połączyć te słowa kluczowe z kontekstem wykonania, ponieważ kontekst wykonania jest ważny w tym wszystkim. Kontekst wykonania składa się z dwóch faz: fazy tworzenia i fazy realizacji. Ponadto każdy kontekst wykonania ma zmienne środowisko i środowisko zewnętrzne (jego środowisko leksykalne).

Podczas fazy tworzenia kontekstu wykonania zmienne var, let i const będą nadal przechowywać zmienną w pamięci o nieokreślonej wartości w środowisku zmiennych danego kontekstu wykonania. Różnica polega na fazie wykonania. Jeśli użyjesz odwołania do zmiennej zdefiniowanej za pomocą var przed przypisaniem jej wartości, zostanie ona po prostu niezdefiniowana. Nie zostanie zgłoszony żaden wyjątek.

Nie można jednak odwoływać się do zmiennej zadeklarowanej za pomocą let lub const, dopóki nie zostanie zadeklarowana. Jeśli spróbujesz go użyć, zanim zostanie zadeklarowany, wyjątek zostanie zgłoszony podczas fazy wykonania kontekstu wykonania. Teraz zmienna nadal będzie w pamięci, dzięki fazie tworzenia kontekstu wykonania, ale silnik nie pozwoli jej użyć:

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

Jeśli zmienna jest zdefiniowana za pomocą var, jeśli aparat nie może znaleźć zmiennej w bieżącym środowisku zmiennym bieżącego kontekstu wykonania, przejdzie w górę łańcucha zasięgu (środowisko zewnętrzne) i sprawdzi zmienne środowisko środowiska zewnętrznego. Jeśli nie może go tam znaleźć, będzie kontynuował przeszukiwanie łańcucha zasięgu. Nie jest tak w przypadku let i const.

Drugą cechą let jest to, że wprowadza zakres bloków. Bloki są zdefiniowane przez nawiasy klamrowe. Przykłady obejmują bloki funkcyjne, jeśli bloki, dla bloków itp. Gdy deklarujesz zmienną z wpuszczeniem wewnątrz bloku, zmienna jest dostępna tylko wewnątrz bloku. W rzeczywistości za każdym razem, gdy blok jest uruchamiany, na przykład w pętli for, tworzy nową zmienną w pamięci.

ES6 wprowadza również słowo kluczowe const do deklarowania zmiennych. const ma również zasięg blokowy. Różnica między let i const polega na tym, że zmienne const muszą być deklarowane za pomocą inicjatora, w przeciwnym razie wygeneruje błąd.

I wreszcie, jeśli chodzi o kontekst wykonania, zmienne zdefiniowane za pomocą var zostaną dołączone do obiektu „this”. W globalnym kontekście wykonania będzie to obiekt okna w przeglądarkach. Nie jest tak w przypadku let lub const.

Donato
źródło
2

Myślę, że warunki i większość przykładów są nieco przytłaczające. Głównym problemem, jaki osobiście miałem z tą różnicą, jest zrozumienie, czym jest „Blok”. W pewnym momencie zdałem sobie sprawę, że blok będzie nawiasami klamrowymi oprócz IFinstrukcji. otwierający nawias {funkcji lub pętli definiuje nowy blok, wszystko zdefiniowane letw nim, nie będzie dostępny po zamykającym nawiasie }tej samej rzeczy (funkcji lub pętli); Mając to na uwadze, łatwiej było zrozumieć:

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

Demencja
źródło
1

Teraz myślę, że lepiej jest scopować zmienne do bloku instrukcji, używając 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
}

Myślę, że ludzie zaczną używać let tutaj, aby mieli podobne zakresy w JavaScript, jak inne języki, Java, C # itp.

Ludzie, którzy nie rozumieli dokładnie zakresu w JavaScript, wcześniej popełnili błąd.

Podnoszenie nie jest obsługiwane przy użyciu let.

Przy takim podejściu błędy obecne w JavaScript są usuwane.

Zobacz ES6 In Depth: let i const, aby lepiej to zrozumieć.

swaraj patil
źródło
Aby uzyskać dogłębne zrozumienie, patrz link - davidwalsh.name/for-and-against-let
swaraj patil
1

W tym artykule wyraźnie określono różnicę między var, let i const

const jest sygnałem, że identyfikator nie zostanie ponownie przypisany.

let, oznacza sygnał, że zmienna może zostać przypisana ponownie, na przykład licznik w pętli lub zamiana wartości w algorytmie. Sygnalizuje również, że zmienna będzie używana tylko w bloku, w którym jest zdefiniowana, co nie zawsze jest całą funkcją zawierającą.

varjest teraz najsłabszym sygnałem dostępnym po zdefiniowaniu zmiennej w JavaScript. Zmienna może, ale nie musi, zostać ponownie przypisana, a zmienna może, ale nie musi być używana do całej funkcji, lub tylko do celów bloku lub pętli.

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

anandharshan
źródło