Введено 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 підняті .Це означає, що декларації завжди знаходяться на вершині сфери застосування.
(**) Змінні масштабу блоку не піднімаються
iIS відомий скрізь у функціональному блоці! Починається як 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
constconstдосить схожий на 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-го видання і, швидше за все, буде в остаточній специфікації.