Pytanie Jaka jest różnica między używaniem "let" i "var" w celu zadeklarowania zmiennej w JavaScript?


Wprowadzono ECMAScript 6 let komunikat. Słyszałem, że opisywana jest jako zmienna "lokalna", ale nadal nie jestem całkiem pewien, jak zachowuje się inaczej niż var słowo kluczowe.

Jakie są różnice? Kiedy powinien let być używane powyżej var?


3259
2018-04-17 20:09


pochodzenie


ECMAScript jest standardem i let jest zawarty w 6. edycja projektu i najprawdopodobniej znajdzie się w ostatecznej specyfikacji. - Richard Ayotte
Widzieć kangax.github.io/es5-compat-table/es6 dla aktualnej matrycy wsparcia funkcji ES6 (w tym let). W momencie pisania Firefoksa, Chrome i IE11 wszystkie go wspierają (chociaż uważam, że implementacja FF nie jest dość standardowa). - Nico Burns
Przez długi czas nie wiedziałem, że vars w pętli for są ograniczone do funkcji, w którą zostały zapakowane. Pamiętam, że wymyśliłem to po raz pierwszy i pomyślałem, że to bardzo głupie. Widzę trochę mocy, ale wiem teraz, jak te dwa mogą być używane z różnych powodów i jak w niektórych przypadkach możesz chcieć użyć var ​​w pętli for i nie mieć tego zakresu do bloku. - Eric Bishard
W miarę doskonalenia obsługi funkcji ES6 problem adopcji ES6 zmienia się ze wsparcia funkcji na różnice wydajności. Takie jak, oto strona, którą znalazłem porównując różnice wydajności między ES6 i ES5. Należy pamiętać, że z czasem ulegną one zmianie wraz z optymalizacją silników pod kątem kodu ES6. - timolawl
To bardzo dobra lektura wesbos.com/javascript-scoping - onmyway133


Odpowiedzi:


Różnica polega na ustalaniu zakresu. var jest dopasowany do najbliższego bloku funkcyjnego i let ma zasięg do najbliższego załączając blok, który może być mniejszy niż blok funkcyjny. Oba są globalne, jeśli są poza jakimkolwiek blokiem.

Również zmienne zadeklarowane przy pomocy let są niedostępne, zanim nie zostaną zadeklarowane w ich zamkniętym bloku. Jak widać w wersji demonstracyjnej, spowoduje to wyjątek ReferenceError.

Próbny: 

var html = '';

write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('\nset variables');

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('\n#### function ####');
  write('\nfunctionVar: ' + functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('\nset variables');

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('\nfunctionVar: ' + functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('\n#### block ####');
  write('\nblockVar: ' + blockVar); //undefined, but visible

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }

  for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
    write('\nblockVar: ' + blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('\nblockVar: ' + blockVar);

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }
}

function write(line) {
  html += (line ? line : '') + '<br />';
}

functionScoped();
blockScoped();

document.getElementById('results').innerHTML = html;
<pre id="results"></pre>

Światowy:

Są bardzo podobne, gdy są używane w podobny sposób poza blokiem funkcyjnym.

let me = 'go';  // globally scoped
var i = 'able'; // globally scoped

Jednak zmienne globalne zdefiniowane przy pomocy let nie zostaną dodane jako właściwości globalne window obiekt taki jak zdefiniowany przy pomocy var.

console.log(window.me); // undefined
console.log(window.i); // 'able'

Funkcjonować:

Są identyczne, gdy są używane w bloku funkcyjnym.

function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!'; //function block scoped
    var sityCheerleading = 'go!'; //function block scoped
}

Blok:

Oto różnica. let jest widoczne tylko w for() pętla i var jest widoczne dla całej funkcji.

function allyIlliterate() {
    //tuce is *not* visible out here

    for( let tuce = 0; tuce < 5; tuce++ ) {
        //tuce is only visible in here (and in the for() parentheses)
        //and there is a separate tuce variable for each iteration of the loop
    }

    //tuce is *not* visible out here
}

function byE40() {
    //nish *is* visible out here

    for( var nish = 0; nish < 5; nish++ ) {
        //nish is visible to the whole function
    }

    //nish *is* visible out here
}

Redeclaration:

Zakładając tryb ścisły, var pozwoli Ci ponownie zadeklarować tę samą zmienną w tym samym zakresie. Z drugiej strony, let nie będzie:

'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, `me` is replaced.

4571
2018-05-27 10:16



Czy celem jest zezwalanie na oświadczenia, aby zwolnić pamięć, gdy nie jest ona potrzebna w danym bloku? - NoBugs
@NoBugs, tak. Zachęca się, że zmienne istnieją tylko tam, gdzie są potrzebne. - batman
let wyrażenie blokowe let (variable declaration) statement jest niestandardowy i zostanie usunięty w przyszłości, bugzilla.mozilla.org/show_bug.cgi?id=1023609. - Gajus
ich jest różnica w zasięgu globalnym: let nie dodawaj właściwości do zmiennej globalnej 2ality.com/2015/02/es6-scoping.html#the_global_object - Yukulélé
@ThinkingStiff: Przejrzyj ten meta posti rozważyć podniesione kwestie techniczne. - Robert Harvey♦


let może być również użyty do uniknięcia problemów z zamknięciami. Wiąże on świeżą wartość, a nie zachowuje dawne odniesienie, jak pokazano w przykładach poniżej.

PRÓBNY

for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

Powyższy kod demonstruje klasyczny problem z zamknięciem języka JavaScript. Odniesienie do i Zmienna jest przechowywana w zamknięciu obsługi kliknięcia, a nie rzeczywistej wartości i.

Każdy pojedynczy moduł obsługi kliknięcia będzie odnosił się do tego samego obiektu, ponieważ istnieje tylko jeden obiekt licznika, który mieści 6, dzięki czemu uzyskujesz sześć przy każdym kliknięciu.

Ogólnym rozwiązaniem jest zawijanie tego w anonimową funkcję i przekazywanie i jako argument. Takich problemów można również uniknąć teraz za pomocą let zamiast var jak pokazano w poniższym kodzie.

PRÓBNY (Testowane w Chrome i Firefox 50)

'use strict';

for(let i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

451
2018-04-17 20:11



To jest naprawdę fajne. Spodziewam się, że "i" zostanie zdefiniowane poza ciałem pętli, które zawiera się w nawiasach i NIE tworzy "zamknięcia" wokół "i". Oczywiście twój przykład dowodzi inaczej. Myślę, że jest to nieco mylące z punktu widzenia składni, ale ten scenariusz jest tak powszechny, że warto go wspierać w ten sposób. Wielkie dzięki za przedstawienie tego. - Karol Kolenda
IE 11 obsługuje let, ale alarmuje "6" dla wszystkich przycisków. Czy masz jakieś źródło mówiące jak let ma się zachowywać? - Jim Hunziker
Wygląda na to, że twoją odpowiedzią jest prawidłowe zachowanie: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - Jim Hunziker
Rzeczywiście jest to typowa pułapka w JavaScript i teraz widzę, dlaczego let byłoby naprawdę przydatne. Ustawienie detektorów zdarzeń w pętli nie wymaga już natychmiastowego wywołania funkcji dla lokalnego określania zakresu i w każdej iteracji. - Adrian Moisa
Użycie "niech" po prostu rozwiązuje ten problem. Tak więc każda iteracja tworzy prywatny, niezależny zakres bloków, ale zmienna "i" może nadal być uszkodzona przez kolejne zmiany w bloku (przyznana zmienna iteratora nie jest zazwyczaj zmienione w bloku, ale inne zadeklarowane zmienne let w bloku mogą być) i każda funkcja zadeklarowana w bloku może, po wywołaniu, uszkodzić wartość "i" dla innych funkcji zadeklarowanych w bloku, ponieważ zrobić mają ten sam prywatny zakres bloków, a więc to samo odniesienie do "i". - gary


Oto jest wyjaśnienie let słowo kluczowe z kilkoma przykładami.

niech działa bardzo podobnie do var. Główna różnica polega na tym, że zakres zmiennej var jest całą funkcją otaczającą

Ten stół na Wikipedii pokazuje, które przeglądarki obsługują JavaScript 1.7.

Zauważ, że obsługują go tylko przeglądarki Mozilla i Chrome. IE, Safari i potencjalnie inne nie.


131
2018-02-23 18:35



Kluczowym fragmentem tekstu z połączonego dokumentu wydaje się być: "niech działa bardzo podobnie do var. Główna różnica polega na tym, że zakres zmiennej var jest całą funkcją otaczającą". - Michael Burr
Chociaż technicznie poprawne jest twierdzenie, że IE go nie obsługuje, bardziej słuszne jest stwierdzenie, że jest to tylko rozszerzenie mozilla. - olliej
@olliej, w rzeczywistości Mozilla jest tuż przed grą. Patrz strona 19 z ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf - Tyler Crompton
@TylerCrompton to tylko zestaw słów zarezerwowanych na lata. Po dodaniu mozilli niech będzie czystym rozszerzeniem mozilla, bez powiązanej specyfikacji. ES6 powinien zdefiniować zachowanie dla instrukcji let, ale to było po tym, jak mozilla wprowadziła składnię. Pamiętaj, moz ma również E4X, który jest całkowicie martwy i tylko moz. - olliej
IE11 dodał wsparcie dla let  msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx - eloyesp


Jaka jest różnica pomiędzy let i var?

  • Zmienna zdefiniowana za pomocą a var oświadczenie jest znane w całym tekście funkcja jest zdefiniowany w, od początku funkcji. (*)
  • Zmienna zdefiniowana za pomocą a let oświadczenie jest znane tylko w blok jest zdefiniowany w, od momentu zdefiniowania dalej. (**)

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 widzimy naszą zmienną j jest znana tylko w pierwszej pętli for, ale nie przed i po. A jednak nasza zmienna i jest znana w całej funkcji.

Należy również wziąć pod uwagę, że zmienne o zakresie bloków nie są znane, zanim nie zostaną zadeklarowane, ponieważ nie zostały podniesione. Nie możesz także redeclare tej samej zmiennej zakresu bloku w tym samym bloku. Powoduje to, że zmienne o ograniczonym zakresie kodu są mniej podatne na błędy niż globalnie lub zmienne o zasięgu funkcjonalnym, które są podnoszone i które nie powodują błędów w przypadku wielu deklaracji.


Czy to bezpieczne w użyciu let dzisiaj?

Niektórzy twierdzą, że w przyszłości będziemy używać TYLKO instrukcji let, a instrukcje var staną się przestarzałe. Guru JavaScript Kyle Simpson napisał bardzo rozbudowany artykuł o tym, dlaczego tak nie jest.

Dziś jednak tak nie jest. Właściwie to musimy zadać sobie pytanie, czy można bezpiecznie używać let komunikat. Odpowiedź na to pytanie zależy od twojego środowiska:

  • Jeśli piszesz kod JavaScript po stronie serwera (Node.js), możesz bezpiecznie korzystać z let komunikat.

  • Jeśli piszesz kod JavaScript po stronie klienta i używasz transpilatora (np Traceur), możesz bezpiecznie korzystać z let oświadczenie, jednak twój kod prawdopodobnie nie będzie optymalny pod względem wydajności.

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

Dzisiaj, 8 czerwca 2018 roku, wciąż jest kilka przeglądarek, które nie obsługują let!

enter image description here


Jak śledzić obsługę przeglądarki

Aby uzyskać aktualny przegląd obsługiwanych przeglądarek let oświadczenie w momencie czytania tej odpowiedzi, patrz to Can I Use strona.


(*) Zmienne globalne i funkcjonalne można zainicjować i używać przed ogłoszeniem, ponieważ są to zmienne JavaScript podniesiony. Oznacza to, że deklaracje są zawsze bardzo ważne.

(**) Zmienne o zakresie blokowym nie są podnoszone


116
2018-06-02 20:59



dotyczące odpowiedzi v4: i Jest znany wszędzie w bloku funkcyjnym! Zaczyna się jako undefined (z powodu podnoszenia) aż do przypisania wartości! ps: let jest również podciągnięty (na górę zawiera blok), ale da mu ReferenceError w przypadku odwołania w bloku przed pierwszym przydziałem. (ps2: Jestem typowym facetem, ale naprawdę nie potrzebujesz średnika po bloku). Biorąc to pod uwagę, dziękuję za dodanie kontroli rzeczywistości dotyczącej wsparcia! - GitaarLAB
@GitaarLAB: Według Mozilla Developer Network : "W ECMAScript 2015, pozwólmy, aby wiązania nie podlegały Variable Hoisting, co oznacza, że ​​deklaracje let nie przesuwają się na początek bieżącego kontekstu wykonania." - W każdym razie, wprowadziłem kilka usprawnień do mojej odpowiedzi, które powinny wyjaśnić różnicę między zachowaniami podnoszenia let i var! - John Slegers
Twoja odpowiedź znacznie się poprawiła (dokładnie sprawdziłem). Zauważ, że ten sam link, do którego się odwołujesz w swoim komentarzu, mówi również: "Zmienna (let) znajduje się w" czasowej martwej strefie " początek bloku do momentu przetworzenia inicjalizacji. "Oznacza to, że" identyfikator "(ciąg tekstowy" zarezerwowany "do wskazania" czegoś ") jest już zarezerwowane w odpowiednim zakresie, w przeciwnym razie stałoby się częścią zakresu głównego / hosta / okna. Dla mnie osobiście "podnoszenie" oznacza jedynie rezerwowanie / łączenie deklarowanych "identyfikatorów" z ich odpowiednim zakresem; z wyłączeniem ich inicjalizacji / przypisania / modyfikacji! - GitaarLAB
I .. + 1. Wspomniany artykuł Kyle'a Simpsona to doskonały czytaj, dziękuję za to! Oczywiste jest również, że "strefa martwa temporalna" to także "TDZ". Jedna ciekawa rzecz, którą chciałbym dodać: przeczytałem na MDN, że let i const byli zaleca się używać tylko wtedy, gdy rzeczywiście potrzebujesz dodatkowej funkcjonalności, ponieważ wymuszenie / sprawdzenie tych dodatkowych funkcji (takich jak const-only) powoduje "więcej pracy" (i dodatkowych węzłów zasięgu w drzewie zakresu) dla (aktualnego) silnika (ów) w celu wymuszenia / sprawdzenia / weryfikacji / instalacji . - GitaarLAB


W przyjętej odpowiedzi brakuje punktu:

{
  let a = 123;
};

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

98
2018-04-17 21:38



Przyjęta odpowiedź wyjaśnia tę kwestię. - TM.
Przyjęta odpowiedź NIE wyjaśnia tego punktu w jej przykładzie. Przyjęta odpowiedź tylko pokazała to w for inicjalizator pętli, radykalnie zawężając zakres stosowania ograniczeń let. Rewizja. - Jon Davis
@ stimpy77 Jednoznacznie stwierdza, że ​​"let's scoped to the nearest enclosing block"; czy należy włączyć w każdy sposób manifesty? - Dave Newton
było wiele przykładów i żadna z nich nie zademonstrowała właściwie sprawy. Mogłem przegłosować zarówno zaakceptowaną odpowiedź, jak i tę? - Jon Davis
Ten wkład pokazuje, że "blok" może być po prostu zbiorem linii ujętych w nawiasy; tj. nie musi być powiązany z żadnym przepływem sterowania, pętlą itp. - webelo


Istnieją pewne subtelne różnice - let Scoping zachowuje się bardziej jak zmienne scoping w mniej więcej innych językach.

na przykład Jest dopasowany do otaczającego bloku, Nie istnieją, zanim nie zostaną zadeklarowane itd.

Jednak warto to zauważyć let jest tylko częścią nowszych implementacji JavaScript i ma różne stopnie obsługa przeglądarki.


40
2018-03-06 10:41



Warto również zauważyć, że ECMAScript jest standardem i let jest zawarty w 6. edycja projektu i najprawdopodobniej znajdzie się w ostatecznej specyfikacji. - Richard Ayotte
Taka jest różnica 3 lat: D - olliej
Po prostu przeszukano to pytanie, aw 2012 r. Wciąż jest tak, że obsługiwane są tylko przeglądarki Mozilli let. Safari, IE i Chome wszystko nie. - pseudosavant
Pomysł przypadkowego stworzenia częściowego bloku blokowego na wypadek jest dobrym punktem, uwaga, let nie podnosi, aby użyć zmiennej zdefiniowanej przez a let zdefiniowane u góry twojego bloku. Jeśli masz if oświadczenie, które jest czymś więcej niż tylko kilkoma liniami kodu, możesz zapomnieć, że nie możesz używać tej zmiennej, dopóki jej nie zdefiniujesz. WSPANIAŁY PUNKT !!! - Eric Bishard
@EricB: yes i no: "W ECMAScript 2015, let  będzie podnosić zmienna na początek bloku. Jednak odwołanie do zmiennej w bloku przed deklaracją zmiennej daje w wyniku ReferenceError (moja uwaga: zamiast dobrego starego undefined). 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 blok podstawowy ". developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - GitaarLAB


Oto przykład różnicy między tymi dwoma elementami (wsparcie rozpoczęte dla chrome): enter image description here

Jak widać var j zmienna nadal ma wartość spoza zakresu pętli for (zakres bloków), ale let i zmienna jest niezdefiniowana poza zasięgiem 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);


39
2017-11-23 22:52



Jakiego narzędzia tu oglądam? - Barton
Chrome devtools - vlio20
Jako twórca apletów pulpitu dla Cynamonu nie byłem narażony na takie błyszczące narzędzia. - Barton


let

Zablokuj zakres

Zmienne zadeklarowane za pomocą let słowo kluczowe ma zasięg blokowy, co oznacza, że ​​są dostępne tylko w blok w którym zostały ogłoszone.

Na najwyższym poziomie (poza funkcją)

Na najwyższym poziomie zmienne zadeklarowane za pomocą let nie twórz właściwości na obiekcie globalnym.

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 funciton (ale poza blokiem), let ma taki sam zakres 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

Zmienne zadeklarowane za pomocą let wewną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

Zmienne zadeklarowane przy pomocy let w 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 zamknięciami

Jeśli użyjesz let zamiast var w pętli, z każdą iteracją otrzymujesz nową zmienną. Oznacza to, że możesz bezpiecznie używać 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);
}

Temporal dead zone

Z powodu czasowa strefa nieczułości, zmienne zadeklarowane za pomocą let nie można uzyskać dostępu, zanim zostaną zadeklarowane. Próba wykonania tego spowoduje zgłoszenie błędu.

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

Bez ponownej deklaracji

Nie możesz zadeklarować tej samej zmiennej kilka razy używając let. Nie możesz również zadeklarować zmiennej za pomocą let z tym samym identyfikatorem, co inna zmienna, 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

const jest bardzo podobny do let-Jest w bloku i ma TDZ. Istnieją jednak dwie rzeczy, które są różne.

Brak ponownego przypisania

Zmienna deklarowana za pomocą const nie 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ś go użyć Object.freeze().

Wymagany jest inicjator

Zawsze musisz podać wartość podczas deklarowania zmiennej za pomocą const.

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

38
2018-01-17 15:11





  • Zmienna nieodnoszona

    let będzie nie podnoś do całego zakresu bloku, w którym się pojawiają. Natomiast var może podnieść się 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, Obie var i let są podnoszone.

  • Zbieranie śmieci

    Zablokuj zakres let jest użyteczny w odniesieniu do zamykania i zbierania śmieci 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){
        //....
    });
    

    The click callback handler nie potrzebuje hugeData zmienna w ogóle. Teoretycznie po process(..) działa, ogromna struktura danych hugeData mogą być zbierane śmieci. Jednak możliwe jest, że niektóre silniki JS będą nadal musiały zachować tę ogromną strukturę, ponieważ click funkcja ma zamknięcie w całym zakresie.

    Jednak zakres bloków może uczynić tę ogromną strukturę danych zbiorem ś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

    let w pętli może ponownie ją wiąże do każdej iteracji pętli, upewniając się, aby ponownie przypisać jej wartość 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);  
    }
    

    Wymień var z let

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

    Bo let stworzyć nowe środowisko leksykalne z tymi nazwami dla a) wyrażenia inicjalizacyjnego b) każdej iteracji (poprzednio do oceny wyrażenia przyrostowego), więcej szczegółów jest tutaj.


20
2018-03-22 14:39



Tak właściwie nadal są podnoszone - Bergi
Yip są podnoszone, ale zachowują się tak, jakby nie zostały podniesione z powodu (bębnowej rolki) Temporal Dead Zone - bardzo dramatycznej nazwy dla identyfikatora, który nie jest dostępny, dopóki nie zostanie zadeklarowany :-) - Drenai
Więc niech się podnosi, ale niedostępna? Czym różni się od "nie podniesionego"? - N-ate
Mam nadzieję, że Brian lub Bergi wrócą, aby odpowiedzieć na to pytanie. Czy deklaracja wynajęcia została podniesiona, ale nie zlecenie? Dzięki! - N-ate
@ N-ate, Oto jest jeden post Bergi, może znajdziesz w niej odpowiedź. - zangw


Główną różnicą jest zakres różnica, natomiast pozwolić może być dostępne tylko wewnątrz zadeklarowanego zakresu, podobnie jak w pętli for, var można uzyskać dostęp na przykład poza pętlą. Z dokumentacji w MDN (przykłady również z MDN):

pozwolić pozwala zadeklarować zmienne o ograniczonym zakresie do bloku, instrukcji lub wyrażenia, w których jest używany. To nie jest podobne do var słowo kluczowe, które definiuje zmienną globalnie lub lokalnie do całej funkcji, niezależnie od zakresu bloku.

Zmienne zadeklarowane przez pozwolić mają w swoim zakresie blok, w którym są zdefiniowane, a także w dowolnych zawartych podblokach. W ten sposób, pozwolić działa bardzo podobnie var. Główną różnicą jest to, że zakres a var zmienna to cała funkcja otaczająca:

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, pozwolić, w odróżnieniu 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, pozwól ograniczyć zakres zmiennej do tego bloku. Zauważ różnicę między var którego zakres znajduje się w funkcji, w której jest 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, że jest to funkcja ECMA6, więc nie jest jeszcze w pełni obsługiwana, więc lepiej zawsze przenosić ją do ECMA5 używając Babel itp ... aby uzyskać więcej informacji na temat wizyty strona babel


15
2017-08-18 00:58





Oto przykład, aby dodać do tego, co inni już napisali. Załóżmy, że chcesz utworzyć szereg funkcji, adderFunctions, gdzie każda funkcja pobiera jeden argument Number i zwraca sumę argumentu i indeksu funkcji w tablicy. Próbuję wygenerować adderFunctions z pętlą za pomocą var słowo kluczowe nie działa tak, jak ktoś może 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żądanego zestawu funkcji, ponieważ izakres wykracza poza iterację for blok, w którym utworzono każdą funkcję. Zamiast tego na końcu pętli i w każdej z funkcji znajduje się zamknięcie iwartość na końcu pętli (1000) dla każdej anonimowej funkcji w adderFunctions. Wcale tego nie chcieliśmy: mamy teraz w pamięci 1000 różnych funkcji z dokładnie takim samym zachowaniem. A jeśli później zaktualizujemy wartość imutacja wpłynie na wszystkie adderFunctions.

Możemy jednak spróbować ponownie używając let słowo kluczowe:

// 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, i jest odbiciem w każdej iteracji pliku for pętla. Każda funkcja zachowuje teraz wartość i w momencie utworzenia funkcji, oraz adderFunctions zachowuje się zgodnie z oczekiwaniami.

Teraz obraz mieszający te dwa zachowania i prawdopodobnie zobaczysz, dlaczego nie zaleca się mieszania nowszego let i const ze starszymi var w tym samym skrypcie. Może to spowodować kilka spektakularnie mylących kodów.

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 przydarzyło. Użyj lancy.

UWAGA: Jest to przykład nauczania mający na celu wykazanie var/let zachowanie w pętlach i z zamkniętymi funkcjami, które również byłyby łatwe do zrozumienia. To byłby straszny sposób dodawania liczb. Jednak ogólną technikę przechwytywania danych w zamknięciach anonimowych funkcji można napotkać w realnym świecie w innych kontekstach. YMMV.


12
2018-05-22 01:09



@aborz: Również bardzo fajna anonimowa składnia funkcji w drugim przykładzie. Właśnie do tego przyzwyczaiłem się w C #. Nauczyłem się czegoś dzisiaj. - Barton
Korekta: Technicznie, tutaj opisano składnię funkcji Arrow => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - Barton
Właściwie to nie potrzebujesz let value = i;. The for Instrukcja tworzy blok leksykalny. - Toothbrush