ECMAScript 6 wprowadził to let
oświadczenie .
Słyszałem, że jest opisywana jako zmienna „lokalna”, ale wciąż nie jestem pewien, jak zachowuje się inaczej niż var
słowo kluczowe.
Jakie są różnice? Kiedy należy let
zużyć var
?
ECMAScript 6 wprowadził to let
oświadczenie .
Słyszałem, że jest opisywana jako zmienna „lokalna”, ale wciąż nie jestem pewien, jak zachowuje się inaczej niż var
słowo kluczowe.
Jakie są różnice? Kiedy należy let
zużyć var
?
Odpowiedzi:
Główną różnicą są zasady określania zakresu. Zmienne zadeklarowane przez var
słowo kluczowe mają zasięg do bezpośredniego ciała funkcji (stąd zakres funkcji), a let
zmienne 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 let
wprowadzenia 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: 3
był 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.
Podczas gdy zmienne zadeklarowane za pomocą var
słowa kluczowego są podnoszone (inicjowane undefined
przed 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();
let
zmienne 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();
Na najwyższym poziomie, let
w 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
W trybie ścisłym var
pozwoli Ci ponownie zadeklarować tę samą zmienną w tym samym zakresie, jednocześnie let
podnoszą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
let
wyrażenie blokowe let (variable declaration) statement
jest niestandardowe i zostanie w przyszłości usunięte, bugzilla.mozilla.org/show_bug.cgi?id=1023609 .
let
moż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 i
zmiennej 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 i
jako argumentu. Takich problemów można teraz również uniknąć, używając let
zamiast tego, var
jak 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>
let
, ale ostrzega „6” dla wszystkich przycisków. Czy masz jakieś źródło, które mówi, jak let
ma się zachowywać?
let
był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 i
przy każdej iteracji.
let
i var
?var
instrukcji jest znana w całej funkcji, w której jest zdefiniowana, od początku funkcji. (*)let
instrukcji 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 j
jest znana tylko w pierwszej pętli for, ale nie przed i po. Jednak nasza zmienna i
jest 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.
let
dziś 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 let
oś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ć let
instrukcji.
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 let
oś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ć let
instrukcji. 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
:
Aby let
zapoznać się z aktualnym przeglądem przeglądarek obsługujących to oświadczenie podczas czytania tej odpowiedzi, zobacz tę Can I Use
stronę .
(*) 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
i
JEST znany wszędzie w bloku funkcyjnym! Zaczyna się jako undefined
(z powodu podnoszenia) do momentu przypisania wartości! ps: let
jest również podnoszony (na górę, w której znajduje się blok), ale da znak, ReferenceError
gdy 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!
let
i var
!
let
i const
został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.
Oto wyjaśnienie let
słowa kluczowego z kilkoma przykładami.
let
działa bardzo podobnievar
. Główną różnicą jest to, że zakresemvar
zmiennej 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.
let
msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
W zaakceptowanej odpowiedzi brakuje punktu:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
for
inicjalizatorze pętli, co radykalnie zawęziło zakres zastosowania ograniczeń let
. Pozytywne.
let
Zmienne zadeklarowane przy użyciu let
sł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 zmienne zadeklarowane za pomocą let
nie 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 (ale poza blokiem) let
ma 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
Do zmiennych zadeklarowanych 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
Do zmiennych zadeklarowanych 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.
Jeśli użyjesz let
zamiast var
w 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);
}
Ze względu na czasową martwą strefę , zmienne zadeklarowane za pomocą let
nie 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;
Nie można zadeklarować tej samej zmiennej wiele razy przy użyciu let
. Nie można również zadeklarować zmiennej za let
pomocą 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
const
jest dość podobny do let
- ma blokowy zasięg i ma TDZ. Istnieją jednak dwie rzeczy, które są różne.
Zmiennej zadeklarowanej 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ś użyć Object.freeze()
.
Zawsze musisz podać wartość, deklarując zmienną za pomocą const
.
const a; // SyntaxError: Missing initializer in const declaration
Oto przykład różnicy między nimi (obsługa właśnie uruchomiona dla chrome):
Jak widać, var j
zmienna nadal ma wartość poza zakresem pętli for (zakres bloku), ale let i
zmienna 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);
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 let
jest to tylko część nowszych implementacji Javascript i ma różne stopnie obsługi przeglądarki .
let
jest uwzględniony w wersji roboczej 6. edycji i najprawdopodobniej będzie w ostatecznej specyfikacji.
let
. Safari, IE i Chome nie wszystkie.
let
nie podnosi, aby użyć zmiennej zdefiniowanej przez let
definicję na górze bloku. Jeśli masz if
instrukcję, 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 !!!
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/…
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
Zmienna bez podnoszenia
let
nie będzie podnosić do całego zakresu bloku, w którym się pojawiają. Przeciwnie, var
moż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 var
i let
są podnoszone .
Zbieranie śmieci
Zakres bloków let
jest 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 click
zwrotna programu obsługi w ogóle nie potrzebuje hugeData
zmiennej. Teoretycznie po process(..)
uruchomieniu ogromna struktura danych hugeData
mogłaby zostać wyrzucona do śmieci. Jednak możliwe jest, że niektóre silniki JS nadal będą musiały zachować tę ogromną strukturę, ponieważ click
funkcja 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
let
w 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ć var
zlet
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Ponieważ let
stwó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 .
Oto przykład dodania do tego, co już napisali inni. Załóżmy, że chcesz utworzyć tablicę funkcji, adderFunctions
w której każda funkcja pobiera pojedynczy argument Number i zwraca sumę argumentu oraz indeks funkcji w tablicy. Próba wygenerowania adderFunctions
za pomocą pętli przy użyciu var
sł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ż i
zakres wykracza poza iterację for
bloku, w którym każda funkcja została utworzona. Zamiast tego na końcu pętli zamknięcie i
w każdej funkcji odnosi się do i
wartoś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 let
sł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 i
odbija się przy każdej iteracji for
pętli. Każda funkcja zachowuje teraz wartość i
w 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 let
i const
starszych var
w 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
/let
w 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.
let value = i;
. for
Instrukcja tworzy leksykalny blok.
Różnica polega na zakresie zmiennych zadeklarowanych z każdą z nich.
W praktyce istnieje szereg przydatnych konsekwencji różnicy w zakresie:
let
zmienne są widoczne tylko w ich najbliższym otaczającym bloku ( { ... }
).let
zmienne są użyteczne tylko w wierszach kodu, które występują po zadeklarowaniu zmiennej (nawet jeśli są podnoszone !).let
zmienne nie mogą być ponownie zadeklarowane przez kolejne var
lub let
.let
Zmienne globalne nie są dodawane do window
obiektu globalnego .let
zmienne są łatwe w użyciu przy zamknięciach (nie powodują warunków wyścigu ).Ograniczenia nałożone przez let
zmniejszają 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 let
zmienne 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.
var
moż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 var
do eksportu może zostać zastąpione, jeśli export
migruje z miejsca transpilatora do podstawowego języka.
1. Brak zastosowania poza najbliższym załączającym blokiem:
ten blok kodu zgłosi błąd odniesienia, ponieważ drugie użycie x
wystę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 var
dziełami.
2. Nie używaj przed deklaracją:
ten blok kodu wyrzuci ReferenceError
przed uruchomieniem kodu, ponieważ x
jest 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 var
analizami i uruchomieniami bez zgłaszania wyjątków.
3. Brak ponownej deklaracji:
Poniższy kod pokazuje, że zmienna zadeklarowana za pomocą let
moż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 var
nie działają dobrze z zamknięciami wewnątrz pętli. Oto prosta pętla, która generuje ciąg wartości, które zmienna i
ma 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 i
zamiast 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
var
zamiast let
, kod jest równoważny z: var i = 0; while (i < 5) { doSomethingLater(); i++; }
i
znajduje się poza zamknięciem, a do czasu doSomethingLater()
wykonania i
został już zwiększony 5 razy, stąd wynik jest i is 5
pięć razy. Dzięki użyciu let
zmienna i
znajduje się w zamknięciu, więc każde wywołanie asynchroniczne otrzymuje własną kopię i
zamiast używać „globalnej”, która została utworzona var
.
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ść i
z nazwą j
wewnątrz funkcji.
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
}
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].
Główna różnica między var
i let
polega na tym, że zmienne zadeklarowane za pomocą var
mają zasięg działania . Natomiast funkcje zadeklarowane za pomocą let
mają 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 testVar
zostanie wywołana, zmienna foo, zadeklarowana za pomocą var
, jest nadal dostępna poza if
instrukcją. Ta zmienna foo
byłaby dostępna wszędzie w zakresie testVar
funkcji .
zmienne z let
:
Gdy druga funkcja testLet
zostanie wywołana, pasek zmiennych, zadeklarowany za pomocą let
, jest dostępny tylko wewnątrz if
instrukcji. Ponieważ zmienne zadeklarowaną let
są bloku o zakresie (blok ma kod pomiędzy klamrami na przykład if{}
, for{}
, function{}
).
let
zmienne nie są podnoszone:Kolejną różnicą pomiędzy var
i let
są 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
let
nie przywiązuje się do window
:Zmienna zadeklarowana let
w zakresie globalnym (który jest kodem, który nie jest funkcją) nie jest dodawana jako właściwość window
obiektu 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
let
zużyćvar
?
Użyj let
nad var
dowolnym 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. var
może być użyty, gdy chcesz, aby zmienna globalna była wyraźnie na window
obiekcie (zawsze dokładnie rozważ, czy jest to naprawdę konieczne).
var
jest zmienną zasięgu globalnego (zdolną do podnoszenia).
let
i const
ma 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
Podczas używania let
let
Kluczowe przywiązuje deklaracji zmiennej do zakresu jakiegokolwiek bloku (powszechnie { .. }
para) to zawarty w. Innymi słowy, let
w sposób dorozumiany hijacks zakres dowolny blok za jego deklaracji zmiennej.
let
zmienne nie mogą być dostępne w window
obiekcie, 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ą.
var
zmienne mogą być dostępne w window
obiekcie, 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 let
i var
jak 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ę.
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 ”
let
? (Zakładam, że masz na myśli IIFE z „funkcją samodzielnego wywoływania”).
hiddenProperty
w konstruktorze? Jest tylko jeden hiddenProperty
dla wszystkich instancji w twojej „klasie”.
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 ↓
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"
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 { 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?
var
.
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.
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
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
}
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 let
początkowo znajduje się w zasięgu globalnym , a kiedy deklarujemy gfoo
ponownie wewnątrz if clause
jego 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 hfoo
zdefiniowany przez var
jest 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.
Jak wspomniano powyżej:
Różnica polega na określeniu zakresu.
var
ma zasięg do najbliższego bloku funkcyjnego ilet
jest 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
. myfunc
zawiera zmienną myvar
równą 10. W moim pierwszym przykładzie sprawdzam, czy myvar
równa się 10 ( myvar==10
). Jeśli tak, deklaruję zmienną myvar
(teraz mam dwie zmienne myvar) za pomocą var
słowa kluczowego i przypisuję jej nową wartość (20). W następnym wierszu wypisuję jego wartość na konsoli. Po bloku warunkowym ponownie wypisuję wartość myvar
na konsoli. Jeśli spojrzysz na wynik myfunc
, myvar
ma wartość równą 20.
Przykład 2:
W moim drugim przykładzie zamiast użycia var
słowa kluczowego w moim bloku warunkowym deklaruję myvar
użycie let
słowa kluczowego. Teraz, kiedy dzwonię myfunc
, otrzymuję dwa różne wyjścia: myvar=20
i myvar=10
.
Różnica jest więc bardzo prosta, tzn. Jej zakres.
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.
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 IF
instrukcji. otwierający nawias {
funkcji lub pętli definiuje nowy blok, wszystko zdefiniowane let
w 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);
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ć.
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ą.
var
jest 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
let
jest zawarty w wersji 6 edycji i najprawdopodobniej będzie w ostatecznej specyfikacji.