Введено ECMAScript 6 це let
твердження .
Я чув, що це описано як "локальну" змінну, але я все ще не зовсім впевнений, як вона поводиться інакше, ніж "the" var
ключове слово.
Які відмінності? Коли слід let
використовувати понад var
?
Введено ECMAScript 6 це let
твердження .
Я чув, що це описано як "локальну" змінну, але я все ще не зовсім впевнений, як вона поводиться інакше, ніж "the" var
ключове слово.
Які відмінності? Коли слід let
використовувати понад var
?
Відповіді:
Основна відмінність - це правила розміщення. Змінні, оголошені за var
ключовим словом, передаються в безпосереднє тіло функції (звідси і область дії), а let
змінні - до прямого блоку, що вкладається, позначається { }
(отже, область блоку).
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
Причина того, що let
ключове слово було введено в мову - сфера функцій, є заплутаною і була одним з основних джерел помилок у JavaScript.
Подивіться на цей приклад з іншого питання про stackoverflow :
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3
був виведений на консоль кожного разу, funcs[j]();
коли анонімні функції прив'язувалися до однієї змінної.
Люди мусили створювати негайно викликані функції для збору правильного значення з циклів, але це також було волохатим.
Хоча змінні, задекларовані за допомогою var
ключового слова, піднімаються (ініціалізуються undefined
до запуску коду), що означає, що вони доступні у своїй охоплюючій області ще до їх оголошення:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
let
змінні не ініціалізуються, поки не буде визначено їх визначення. Доступ до них перед ініціалізацією призводить до а ReferenceError
. Змінна, про яку йдеться, знаходиться у "тимчасовій мертвій зоні" від початку блоку до обробки ініціалізації.
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
На верхньому рівні, let
на відміну від цього var
, не створюється властивість глобального об'єкта:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
У суворому режимі var
дозволять вам повторно оголосити ту саму змінну в тій же області, let
піднімаючи 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
Вираз блоку let (variable declaration) statement
є нестандартним і його буде видалено в майбутньому, bugzilla.mozilla.org/show_bug.cgi?id=1023609 .
let
також можна використовувати, щоб уникнути проблем із закриттями. Він пов'язує свіжу цінність, а не зберігає старі посилання, як показано в прикладах нижче.
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>
Код вище демонструє класичну проблему закриття JavaScript. Посилання на i
змінну зберігається у закритті обробника кліків, а не у фактичному значенні i
.
Кожен обробник кожного клацання буде посилатися на один і той же об'єкт, тому що існує лише один лічильник, який містить 6, так що ви отримуєте шість за кожен клік.
Загальне рішення полягає в тому, щоб обернути це в анонімну функцію і i
подати як аргумент. Таких проблем також можна уникнути, використовуючи let
замість цьогоvar
як показано в коді нижче.
(Випробувано в Chrome і 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
, але він попереджає "6" для всіх кнопок. Чи є у вас якесь джерело, яке говорить про те, як let
слід поводитися?
let
було б дуже корисно. Налаштування слухачів подій у циклі більше не вимагає прямого виклику функції вираження для локального визначення часу i
на кожній ітерації.
let
і var
?var
оператора, відома у всій функції, в якій вона визначена, з початку функції.(*)let
оператора, відома лише в блоці, в якому вона визначена, з моменту її визначення. (**)Щоб зрозуміти різницю, врахуйте наступний код:
// 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
Тут ми можемо побачити, що наша змінна j
відома лише в першому для циклу, але не до і після. І все-таки наша зміннаi
відома у всій функції.
Крім того, врахуйте, що змінні в масштабі блоку не відомі до їх оголошення, оскільки вони не піднімаються. Вам також не дозволяється повторно декларувати одну і ту ж змінну, що охоплюється в межах одного блоку. Це робить змінні, що охоплюють блок, менше схильних до помилок, ніж глобальні або функціонально обмежені змінні, які піднімаються і які не створюють помилок у випадку численних оголошень.
let
сьогодні?Деякі люди стверджують, що в майбутньому ми будемо використовувати ТОЛЬКІ випуски висловлювань, а вар-заяви стануть застарілими. Гуру JavaScript Кайл Сімпсон написав дуже детальну статтю про те, чому він вважає, що так не буде .
Однак сьогодні це точно не так. Насправді нам потрібно насправді запитати себе, чи безпечно використовувати let
вислів. Відповідь на це питання залежить від вашого оточення:
Якщо ви пишете код JavaScript на стороні сервера ( Node.js ), можете сміливо використовувати let
оператор.
Якщо ви пишете код на JavaScript на стороні клієнта і використовуєте транспілер на базі браузера (наприклад, Traceur або автономна версія ), ви можете сміливо використовувати let
виписку, однак ваш код, швидше за все, буде оптимальним щодо продуктивності.
Якщо ви пишете JavaScript-код на стороні клієнта та використовуєте транслятор на основі вузла (наприклад, скрипт оболонки прослідковування або Babel ), ви можете сміливо використовувати let
оператор. Оскільки ваш браузер знатиме лише про перекладений код, недоліки в роботі повинні бути обмеженими.
Якщо ви пишете код JavaScript на стороні клієнта і не використовуєте транспілятор, вам потрібно врахувати підтримку браузера.
Є ще деякі браузери, які взагалі не підтримують let
:
Щоб переглянути оновлений огляд того, який браузер підтримує let
заяву під час прочитання цієї відповіді, дивіться цю Can I Use
сторінку .
(*) Змінні на глобальному та функціональному рівні можуть бути ініціалізовані та використані до їх оголошення, оскільки змінні JavaScript підняті .Це означає, що декларації завжди знаходяться на вершині сфери застосування.
(**) Змінні масштабу блоку не піднімаються
i
IS відомий скрізь у функціональному блоці! Починається як undefined
(за рахунок підйому), поки ви не призначите значення! ps: let
також піднімається (до вершини, що містить блок), але дасть знак ReferenceError
при посиланні в блоці перед першим призначенням. (ps2: Я добрий хлопець з комою, але вам не потрібна крапка з комою після блоку). Це було сказано, дякую, що додав перевірку реальності щодо підтримки!
let
та var
!
let
і const
було рекомендовано використовувати лише коли ви на насправді потребують додаткової функціональності , оскільки виконання / перевірка ці додаткові функції (наприклад , тільки для запису сопзЬ) результату в «більше роботи '(та додаткові вузли області в дереві діапазону) для (поточного) двигуна (и) для забезпечення / перевірки / перевірки / налаштування.
Ось пояснення let
ключового слова з деякими прикладами.
let
працює дуже подобаєтьсяvar
. Основна відмінність полягає в тому, що сферою діїvar
змінної є вся функція, що огороджує
Ця таблиця у Вікіпедії показує, які браузери підтримують Javascript 1.7.
Зауважте, що підтримують його лише браузери Mozilla та Chrome. IE, Safari і потенційно інші цього не роблять.
let
msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
У прийнятій відповіді відсутня крапка:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
for
ініціалізаторі циклу, різко звузивши сферу застосування обмежень let
. Отримано.
let
Змінні, задекларовані за допомогою let
ключового слова, мають блок-діапазон, а це означає, що вони доступні лише в блоці, в якому вони були оголошені.
На верхньому рівні змінні, оголошені за допомогою let
, не створюють властивостей у глобальному об'єкті.
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined
Всередині функції (але поза блоком) let
має таку ж область, як і 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
До змінних, оголошених використанням let
всередині блоку, не можна отримати доступ поза цим блоком.
{
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
На змінні, оголошені let
в циклі, можна посилатися лише всередині цього циклу.
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.
Якщо ви використовуєте let
замість var
циклу, з кожною ітерацією ви отримуєте нову змінну. Це означає, що ви можете сміливо використовувати застібку всередині циклу.
// 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);
}
Через тимчасову мертву зону до змінних, оголошених за допомогою, let
не можна отримати доступ до їх оголошення. Спроба зробити це призводить до помилки.
console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;
Ви не можете оголосити одну і ту ж змінну кілька разів, використовуючи let
. Ви також не можете оголосити змінну, використовуючи let
той самий ідентифікатор, що й іншу змінну, яку було оголошено використанням 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
досить схожий на let
блок-діапазон і має TDZ. Однак є дві речі, які відрізняються.
Змінна, оголошена використанням, const
не може бути призначена повторно.
const a = 42;
a = 43; // TypeError: Assignment to constant variable.
Зауважте, що це не означає, що значення незмінне. Його властивості все-таки можна змінити.
const obj = {};
obj.a = 42;
console.log(obj.a); // 42
Якщо ви хочете мати незмінний об’єкт, вам слід скористатися Object.freeze()
.
Ви завжди повинні вказати значення при оголошенні змінної за допомогою const
.
const a; // SyntaxError: Missing initializer in const declaration
Ось приклад різниці між цими (підтримка, щойно розпочалася для chrome):
Як ви бачите, var j
змінна все ще має значення поза межами області циклу (Block Scope), але let i
змінна не визначена за межами для циклу для циклу.
"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);
Існують деякі тонкі відмінності - let
масштабування ведеться більше, як змінне масштабування в більш-менш інших мовах.
наприклад, він охоплює блок, що додається, вони не існують до того, як вони оголошені тощо.
Однак варто зазначити, що let
це лише частина новіших реалізацій Javascript та підтримує браузер різного ступеня .
let
входить в проект 6-го видання і, швидше за все, буде в остаточній специфікації.
let
. Сафарі, IE та Chome - все це не так.
let
не використовуйте змінну, визначену let
визначеною у верхній частині блоку. Якщо у вас є if
твердження, яке перевищує лише кілька рядків коду, ви можете забути, що ви не можете використовувати цю змінну до того моменту, як вона буде визначена. ВЕЛИКИЙ БОК !!!
let
буде піднята змінна у верхній частині блоку. Однак, посилання на змінну в блоці до оголошення змінної призводить до ReferenceError (моя примітка: замість хорошого старого undefined
). змінна знаходиться у "тимчасовій мертвій зоні" від початку блоку до обробки декларації. " Те ж саме стосується і "заяви операторів перемикання, оскільки є лише один базовий блок". Джерело: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Основна відмінність полягає в різниці в області , в той час як нехай це може бути доступно лише в межах заявленої області, як, наприклад, у циклі, наприклад, до var можна отримати доступ за межами циклу. З документації в MDN (приклади також з MDN):
нехай дозволяє оголошувати змінні, обмежені за обсягом блоком, оператором або виразом, для якого він використовується. Це на відміну від вар ключового слова , яке визначає змінну глобально або локально для всієї функції незалежно від області блоку.
Змінні, оголошені let, мають в своїй області блок, в якому вони визначені, а також у будь-яких містяться підблоках. Таким чином, нехай працює дуже схоже на var . Основна відмінність полягає в тому, що сфера застосування змінної var - це вся функція, що додає:
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
}`
На найвищому рівні програм та функцій, нехай , на відміну від var , не створює властивості глобального об'єкта. Наприклад:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
Якщо використовується всередині блоку, нехай обмежує область змінної цим блоком. Зверніть увагу на різницю між var , сфера дії якої знаходиться всередині функції, де вона оголошена.
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
Також не забувайте, що це функція ECMA6, тому вона ще не повністю підтримується, тому краще завжди перекладати її на ECMA5, використовуючи Babel і т.д. ... для отримання додаткової інформації про відвідування веб-сайту babel
Змінна не піднімається
let
буде НЕ талі на весь обсяг блоку вони з'являються в. На відміну від цього , var
може підняти , як показано нижче.
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
Власне, Пер @ Бергі, обидва var
і let
піднімаються .
Збір сміття
let
Корисний блок блоку стосується закриття та вивезення сміття для повернення пам'яті. Подумайте,
function process(data) {
//...
}
var hugeData = { .. };
process(hugeData);
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
click
Як передзвонити обробника не потребує hugeData
змінному взагалі. Теоретично після process(..)
запусків величезна структура даних hugeData
може збирати сміття. Однак можливо, що деяким двигунам JS все ж доведеться зберегти цю величезну структуру, оскільки click
функція має закриття по всій області.
Однак область блоку може зробити цю величезну структуру даних зібраним сміттям.
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
петлі
let
у циклі може повторно прив'язувати його до кожної ітерації циклу, переконуючись повторно призначити йому значення з кінця попередньої ітерації циклу. Подумайте,
// print '5' 5 times
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Однак замініть var
наlet
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Оскільки let
створити нове лексичне середовище з тими іменами для а) початкового виразу b) кожної ітерації (попередньо для оцінки виразного збільшення), тут детальніше .
Ось приклад для додавання до того, що вже написали інші. Припустимо, ви хочете створити масив функцій, adderFunctions
де кожна функція приймає один аргумент Number і повертає в масив суму аргументу та індекс функції. Спроба створення adderFunctions
з циклом за допомогою var
ключового слова не працюватиме так, як хтось може наївно очікувати:
// 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
Наведений вище процес не генерує потрібний масив функцій, оскільки i
область дії виходить за межі ітерації for
блоку, в якому створена кожна функція. Замість цього, наприкінці циклу, i
закриття кожної функції посилається на i
значення 'в кінці циклу (1000) для кожної анонімної функції в adderFunctions
. Це зовсім не те, чого ми хотіли: зараз у нас є масив з 1000 різних функцій в пам'яті з точно такою ж поведінкою. І якщо ми згодом оновимо значення i
, мутація вплине на всіadderFunctions
.
Однак ми можемо спробувати ще раз за допомогою let
ключового слова:
// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];
for (let i = 0; i < 1000; i++) {
// NOTE: We're using the newer arrow function syntax this time, but
// using the "function(x) { ..." syntax from the previous example
// here would not change the behavior shown.
adderFunctions[i] = x => x + i;
}
const add12 = adderFunctions[12];
// Yay! The behavior is as expected.
console.log(add12(8) === 20); // => true
// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined
Цього разу i
відбувається відскок на кожній ітерації for
циклу. Кожна функція тепер зберігає значення i
на момент створення функції та adderFunctions
поводиться так, як очікувалося.
Тепер зображення змішують дві поведінки, і ви, мабуть, побачите, чому не рекомендується змішувати новіші let
та const
старіші var
в одному сценарії. Це може призвести до надзвичайно заплутаного коду.
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
Не дозволяйте цього статися з вами. Використовуйте підводку.
ПРИМІТКА. Це приклад навчання, призначений для демонстрації
var
/let
поведінки в циклі та із закриттям функцій, які також легко зрозуміти. Це було б жахливим способом додавання чисел. Але загальна техніка збору даних в анонімних функціях закриття може зустрічатися в реальному світі в інших контекстах. YMMV.
let value = i;
. for
Оператор створює лексичний блок.
Різниця полягає в обсязі змінних, оголошених з кожною.
На практиці існує ряд корисних наслідків різниці в обсязі:
let
змінні видимі лише в найближчому блоку, що їх додає ( { ... }
).let
змінні можуть бути використані лише у рядках коду, які виникають після оголошення змінної (навіть якщо вони підняті !).let
змінні не можуть бути повторно оголошений на наступний var
або let
.let
змінні не додаються до глобального window
об’єкта.let
змінні прості у використанні при закритті (вони не викликають перегонних умов ).Обмеження, що вводяться завдяки let
зменшенню видимості змінних та збільшують ймовірність того, що несподівані зіткнення імен будуть знайдені рано. Це полегшує відстеження та міркування змінних, включаючи їх доступність (допомагаючи відновити невикористану пам'ять).
Отже, let
менші шанси змінні можуть спричинити проблеми при використанні у великих програмах або коли незалежно розроблені рамки поєднуються новими та несподіваними способами.
var
може все-таки бути корисним, якщо ви впевнені, що хочете отримати ефект єдиного прив'язки під час використання замикання в циклі (№5) або для оголошення зовнішніх видимих глобальних змінних у вашому коді (№4). Використання var
для експорту може бути витіснено, якщо export
мігрує з транспілерного простору та на основну мову.
1. Немає використання поза найближчим огороджувальним блоком:
Цей блок коду призведе до помилки посилання, оскільки друге використання x
відбувається поза блоком, де воно оголошено за допомогою let
:
{
let x = 1;
}
console.log(`x is ${x}`); // ReferenceError during parsing: "x is not defined".
На відміну від того ж прикладу з var
творами.
2. Немає використання перед оголошенням:
Цей блок коду буде передбачати код, ReferenceError
перш ніж його можна буде запустити x
:
{
x = x + 1; // ReferenceError during parsing: "x is not defined".
let x;
console.log(`x is ${x}`); // Never runs.
}
На відміну від цього, той самий приклад з var
аналізами та запуском без викидів.
3. Немає передекларації:
Наступний код демонструє, що змінна, декларована за допомогою, let
не може бути передекларована пізніше:
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
4. Глобали, не приєднані до 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. Легке використання із закриттями:
Змінні, задекларовані з var
, не працюють добре із замиканнями всередині циклів. Ось простий цикл, який виводить послідовність значень змінної i
у різні моменти часу:
for (let i = 0; i < 5; i++) {
console.log(`i is ${i}`), 125/*ms*/);
}
Зокрема, це результати:
i is 0
i is 1
i is 2
i is 3
i is 4
У JavaScript ми часто використовуємо змінні значно пізніше, ніж коли вони створюються. Коли ми демонструємо це, затримуючи вихід із закриттям, переданим на setTimeout
:
for (let i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... вихід залишається незмінним, поки ми дотримуємося let
. На противагу цьому, якби ми використовували var i
замість цього:
for (var i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... цикл несподівано виводить "i is 5" п'ять разів:
i is 5
i is 5
i is 5
i is 5
i is 5
var
замість let
, код еквівалентний: var i = 0; while (i < 5) { doSomethingLater(); i++; }
i
знаходиться поза закриттям, і за часом, який doSomethingLater()
виконується, i
вже збільшується в 5 разів, отже, вихід виходить у i is 5
п’ять разів. Використовуючи let
, змінна i
знаходиться в межах закриття, тому кожен виклик асинхронізації отримує власну копію, i
а не використовує "глобальну", створену за допомогою var
.
for
. Більш точною трансформацією, хоча і більш складною, є класична for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(
i $ {j}, ), 125/*ms*/); })(i); }
яка вводить "запис-активацію функції", щоб зберегти кожне значення i
з назвою j
всередині функції.
Нехай наступні дві функції показують різницю:
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
Це цікаво, оскільки дозволяє нам робити щось подібне:
(() => {
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++);
}
}
}
})();
Що призводить до підрахунку [0, 7].
Тоді як
(() => {
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++);
}
}
}
})();
Число лише [0, 1].
Основна відмінність між var
і let
є те , що змінним , оголошеним з var
є функція області дії . Тоді як функції, задекларовані з, let
є об'єктом блоку . Наприклад:
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
змінні з var
:
Коли перша функція testVar
отримує виклик змінної foo, оголошеної за допомогою var
, все ще доступна поза if
оператором. Ця змінна foo
була б доступна всюди в межах testVar
функції .
змінні з let
:
Коли друга функція testLet
викликається смужкою змінної, оголошеною з let
, доступна лише всередині if
оператора. Оскільки змінні, задекларовані з, let
є об'єктом блоку (де блок є кодом між фігурними дужками, наприкладif{}
, for{}
, function{}
).
let
змінні не піднімаються:Ще одна відмінність між var
і let
є змінними з оголошеним зlet
що не мають підйому . Приклад - найкращий спосіб проілюструвати таку поведінку:
змінні з let
не піднімаються:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
змінні з var
do отримують:
console.log(varVar);
var varVar = 10;
// logs undefined, the variable gets hoisted
let
не приєднується до window
:Змінна, оголошена let
в глобальній області (що є кодом, який не є функцією), не додається як властивість глобального window
об'єкта. Наприклад (цей код знаходиться в глобальному масштабі):
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
Коли слід
let
використовувати понадvar
?
Використовуйте let
більше, var
коли можете, тому що це просто більш чітко визначено. Це зменшує потенційні конфлікти імен, які можуть виникнути при роботі з великою кількістю змінних. var
може використовуватися, коли ви хочете, щоб глобальна змінна явно знаходилася на window
об'єкті (завжди уважно розглядайте, чи це дійсно необхідно).
var
- це змінна глобальна сфера (з можливістю підйому).
let
і const
є блоком області дії.
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
При використанні let
let
Ключове слово надає оголошення змінної в рамках якої - небудь блок (зазвичай є{ .. }
пара) він міститься. Іншими словами, let
неявно викрадає обсяг будь-якого блоку для його оголошення змінної.
let
не можна отримати доступ до змінних в window
об'єкті, оскільки до них не можна отримати глобальний доступ.
function a(){
{ // this is the Max Scope for let variable
let x = 12;
}
console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined
При використанні var
var
а змінні в ES5 мають області функцій, тобто змінні дійсні в межах функції, а не за межами самої функції.
var
доступ до змінних можна отримати в window
об'єкті, оскільки до них не можна отримати глобальний доступ.
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
Якщо ви хочете дізнатися більше, продовжуйте читати нижче
Одного з найвідоміших питань щодо інтерв'ю щодо обсягу також може бути достатнім для точного використання let
таvar
як нижче;
При використанні let
for (let i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 0 to 9, that is literally AWW!!!
},
100 * i);
}
Це тому, що при використанні let
для кожної ітерації циклу змінна визначається областю та має власну копію.
При використанні var
for (var i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 10 times 10
},
100 * i);
}
Це тому, що при використанні var
для кожної ітерації циклу змінна визначається областю та має спільну копію.
Якщо я читаю специфікації правильно, то, на let
щастя, можна також скористатися, щоб уникнути функцій самовиклику, що використовуються для імітації лише приватних членів - популярний шаблон дизайну, який зменшує читабельність коду, ускладнює налагодження, що не додає реального захисту коду чи іншої вигоди - за винятком можливо задоволення когось бажання семантики, тому перестаньте її використовувати. / рент
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
Див. Розділ " Емуляція приватних інтерфейсів "
let
? (Я припускаю, що ви маєте на увазі IIFE з функцією "самонавіювання".)
hiddenProperty
в конструкторі? Є лише один hiddenProperty
для всіх екземплярів вашого "класу".
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
Пісочниця для гри навколо ↓
Деякі хаки з 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;
? ви створюєте новий об’єкт із 3 типом властивостей / ім'ям / значенням та ініціалізуєте їх зі значеннями властивостей з вузла?
var
.
нехай проти вар. Вся справа в обсязі .
змінні var є глобальними і до них можна отримати доступ в основному скрізь, тоді як дозволені змінні не є глобальними і існують лише до тих пір, поки не закриє їх дужка.
Дивіться мій приклад нижче та зазначте, як змінна лев (нехай) по-різному діє в двох console.logs; він стає поза сферою дії у 2-й консолі.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. Ці функції пояснять різницю простим способом.
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
}
Нижче показано, як "нехай" і "вар" відрізняються в області застосування:
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
, визначене let
спочатку, знаходиться в глобальній області , і коли ми gfoo
знову оголошуємо всередині if clause
його області змінені, і коли нове значення присвоюється змінній всередині цієї області, це не впливає на глобальну область.
Тоді як hfoo
, дефінітоване " var
спочатку" знаходиться в глобальному масштабі , але знову ж таки, коли ми оголошуємо його всередині if clause
, воно вважає глобальний обсяг hfoo, хоча для його оголошення знову використовувався var. І коли ми знову присвоюємо його значення, ми бачимо, що глобальний масштаб hfoo також впливає. Це головна відмінність.
Як зазначено вище:
Різниця полягає в оцінці.
var
присвоєно до найближчого функціонального блоку іlet
розміщено до найближчого огороджувального блоку , який може бути меншим, ніж функціональний блок. Обидва є глобальними, якщо вони є поза будь-яким блоком. Подивимось приклад:
Приклад1:
У обох моїх прикладах у мене є функція myfunc
. myfunc
містить змінну, myvar
рівну 10. У своєму першому прикладі я перевіряю, чи myvar
дорівнює 10 ( myvar==10
). Якщо так, я по-аганськи оголошую змінну myvar
(зараз у мене є дві мінні змінні) за допомогою var
ключового слова та присвоюю їй нове значення (20). У наступному рядку я друкую його значення на моїй консолі. Після умовного блоку я знову надрукую значення myvar
на своїй консолі. Якщо ви подивитеся на вихід myfunc
, myvar
значення має рівне 20.
Example2:
У моєму другому прикладі замість використання var
ключового слова в моєму умовному блоці я оголошую myvar
використання let
ключового слова. Тепер, коли я телефоную, myfunc
я отримую два різні виходи: myvar=20
і myvar=10
.
Отже різниця дуже проста, тобто її сфера застосування.
Я хочу зв’язати ці ключові слова з контекстом виконання, оскільки контекст виконання є важливим у всьому цьому. Контекст виконання має дві фази: фазу створення та фазу виконання. Крім того, кожен контекст виконання має змінне середовище та зовнішнє середовище (його лексичне середовище).
Під час фази створення контексту виконання var, let і const все ще зберігатиме свою змінну в пам'яті з невизначеним значенням у змінному середовищі заданого контексту виконання. Різниця полягає у фазі виконання. Якщо ви використовуєте посилання на змінну, визначену з var до того, як їй буде призначено значення, вона буде просто невизначеною. Виняток не буде піднято.
Однак ви не можете посилатися на змінну, оголошену let або const, поки вона не буде оголошена. Якщо ви спробуєте використати його до того, як він буде оголошений, виняток буде піднято під час фази виконання контексту виконання. Тепер ця змінна все ще залишиться в пам'яті, люб’язно надавши Фазу створення контексту виконання, але Двигун не дозволить вам використовувати її:
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
Якщо змінна, визначена з var, якщо Engine не може знайти змінну в змінному середовищі поточного контексту виконання, тоді вона підніметься вгору по ланцюгу областей (Зовнішнє середовище) і перевірить змінну Середовище зовнішнього середовища на змінну. Якщо він не зможе знайти його там, він продовжить пошук у Сфера застосування. Це не стосується дозволу та const.
Друга особливість let - це введення області блоку. Блоки визначаються фігурними дужками. Приклади включають функціональні блоки, якщо блоки, для блоків тощо. Коли ви оголошуєте змінну з пустим всередину блоку, змінна доступна лише всередині блоку. Насправді, кожного разу, коли блок запускається, наприклад, у циклі for for, він створюватиме нову змінну в пам'яті.
ES6 також вводить ключове слово const для декларування змінних. const також є блок-діапазоном. Різниця між let і const полягає в тому, що змінні const потрібно оголосити за допомогою ініціалізатора, або це призведе до помилки.
І, нарешті, коли мова йде про контекст виконання, змінні, визначені var, будуть приєднані до об'єкта "цей". У глобальному контексті виконання це буде віконний об’єкт у браузерах. Це не стосується дозволу чи const.
Я думаю, що терміни та більшість прикладів трохи непосильні. Основне питання, яке я мав особисто, з різницею - розуміння, що таке "Блок". У якийсь момент я зрозумів, що блоком будуть будь-які фігурні дужки, крім IF
заяви. відкриваюча дужка {
функції або циклу визначатиме новий блок, все, що визначено let
в ньому, буде недоступним після дужки, що закривається, }
тієї ж речі (функції або циклу); Зважаючи на це, було легше зрозуміти:
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);
Тепер я думаю, що є кращим розміщення змінних до блоку операторів, використовуючи 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
}
Я думаю, що люди почнуть використовувати пускати сюди після того, щоб вони мали аналогічні показники в JavaScript, як інші мови, Java, C # тощо.
Люди, які не мають чіткого розуміння щодо того, як скористатися JavaScript, раніше робили помилку.
Піднімання не підтримується використанням let
.
При такому підході помилки, наявні в JavaScript, видаляються.
Дивіться ES6 в глибині: дозвольте і погодьтесь, щоб зрозуміти це краще.
Ця стаття чітко визначає різницю між var, let та const
const
- це сигнал про те, що ідентифікатор не буде перепризначений.
let
, є сигналом про те, що змінна може бути переназначена, наприклад, лічильник у циклі або зміна значення в алгоритмі. Це також сигналізує про те, що змінна буде використовуватися лише у визначеному для неї блоці, який не завжди є цілою функцією, що містить.
var
зараз найслабший доступний сигнал, коли ви визначаєте змінну в JavaScript. Змінна може бути або не може бути перепризначена, і ця зміна може або не може використовуватися для всієї функції, або просто для цілей блоку або циклу.
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
міститься в проекті 6-го видання і, швидше за все, буде в остаточній специфікації.