В чем разница между использованием "let" и "var" для объявления переменной в JavaScript?
ECMAScript 6 представил let
заявление. Я слышал, что она описана как "локальная" переменная, но я все еще не совсем уверен, как она ведет себя иначе, чем var
ключевое слово.
Какие есть отличия? Когда следует let
использоваться более var
?
45 ответов
Разница в том, var
ограничен до ближайшего функционального блока и let
ограничен ближайшим включающим блоком, который может быть меньше функционального блока. Оба являются глобальными, если находятся вне какого-либо блока.
Кроме того, переменные объявлены с let
недоступны до тех пор, пока они не будут объявлены в своем блоке. Как видно из демонстрации, это вызовет исключение ReferenceError.
Демо:
var html = '';
write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible
try {
write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
write('globalLet: exception');
}
write('\nset variables');
var globalVar = 'globalVar';
let globalLet = 'globalLet';
write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);
function functionScoped() {
write('\n#### function ####');
write('\nfunctionVar: ' + functionVar); //undefined, but visible
try {
write('functionLet: ' + functionLet); //undefined, *not* visible
} catch (exception) {
write('functionLet: exception');
}
write('\nset variables');
var functionVar = 'functionVar';
let functionLet = 'functionLet';
write('\nfunctionVar: ' + functionVar);
write('functionLet: ' + functionLet);
}
function blockScoped() {
write('\n#### block ####');
write('\nblockVar: ' + blockVar); //undefined, but visible
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
write('\nblockVar: ' + blockVar); // visible here and whole function
};
for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
write('blockLet: ' + blockLet); // visible only here
};
write('\nblockVar: ' + blockVar);
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
}
function write(line) {
html += (line ? line : '') + '<br />';
}
functionScoped();
blockScoped();
document.getElementById('results').innerHTML = html;
<pre id="results"></pre>
Глобальный:
Они очень похожи, когда используются как это вне функционального блока.
let me = 'go'; // globally scoped
var i = 'able'; // globally scoped
Тем не менее, глобальные переменные, определенные с let
не будут добавлены в качестве свойств на глобальном window
объект, как те, которые определены с var
,
console.log(window.me); // undefined
console.log(window.i); // 'able'
Функция:
Они идентичны при использовании таким образом в функциональном блоке.
function ingWithinEstablishedParameters() {
let terOfRecommendation = 'awesome worker!'; //function block scoped
var sityCheerleading = 'go!'; //function block scoped
}
Блок:
Здесь есть разница. let
виден только в for()
петля и var
виден всей функции.
function allyIlliterate() {
//tuce is *not* visible out here
for( let tuce = 0; tuce < 5; tuce++ ) {
//tuce is only visible in here (and in the for() parentheses)
//and there is a separate tuce variable for each iteration of the loop
}
//tuce is *not* visible out here
}
function byE40() {
//nish *is* visible out here
for( var nish = 0; nish < 5; nish++ ) {
//nish is visible to the whole function
}
//nish *is* visible out here
}
переопределение:
Предполагая строгий режим, var
позволит вам повторно объявить одну и ту же переменную в той же области видимости. С другой стороны, let
не буду:
'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, `me` is replaced.
let
может также использоваться, чтобы избежать проблем с закрытием. Он связывает свежую ценность, а не сохраняет старую ссылку, как показано в примерах ниже.
for(var i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(i) })
}
Код выше демонстрирует классическую проблему закрытия JavaScript. Ссылка на i
переменная хранится в закрытии обработчика кликов, а не фактическое значение i
,
Каждый обработчик одного клика будет ссылаться на один и тот же объект, потому что есть только один встречный объект, который содержит 6, так что вы получаете шесть на каждый клик.
Обходной путь - обернуть это в анонимную функцию и передать i
в качестве аргумента. Таких проблем можно также избежать, используя let
вместо var
как показано в коде ниже.
ДЕМО (протестировано в Chrome и Firefox 50)
'use strict';
for(let i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(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
известен только в первом цикле for, но не до и после. Тем не менее, наша переменная i
известен во всей функции.
Также учтите, что переменные в области блока не известны до того, как они объявлены, потому что они не подняты. Вам также не разрешается повторно объявлять одну и ту же переменную области видимости в одном и том же блоке. Это делает переменные области блока менее подверженными ошибкам, чем переменные глобальной или функциональной области, которые перемещаются и которые не выдают никаких ошибок в случае нескольких объявлений.
Безопасно ли использовать let
сегодня?
Некоторые люди утверждают, что в будущем мы будем использовать ТОЛЬКО операторы let, а операторы var станут устаревшими. Гуру JavaScript Кайл Симпсон написал очень сложную статью о том, почему это не так.
Сегодня, однако, это определенно не так. На самом деле, нам нужно спросить себя, безопасно ли использовать let
заявление. Ответ на этот вопрос зависит от вашей среды:
Если вы пишете код JavaScript на стороне сервера ( Node.js), вы можете безопасно использовать
let
заявление.Если вы пишете код JavaScript на стороне клиента и используете транспортер (например, Traceur), вы можете смело использовать
let
заявление, однако ваш код, вероятно, будет не оптимальным с точки зрения производительности.Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.
Сегодня, 8 июня 2018 года, есть еще несколько браузеров, которые не поддерживают let
!
Как отслеживать поддержку браузера
Для актуального обзора, какие браузеры поддерживают let
заявление во время прочтения этого ответа, см. это Can I Use
страница
(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до того, как объявлены, потому что переменные JavaScript подняты. Это означает, что объявления всегда находятся на вершине области видимости.
(**) Переменные области видимости не подняты
Вот объяснение let
Ключевое слово с некоторыми примерами.
пусть работает очень похоже на вар. Основное различие заключается в том, что область действия переменной var - это вся включающая функция
Эта таблица в Википедии показывает, какие браузеры поддерживают Javascript 1.7.
Обратите внимание, что только браузеры Mozilla и Chrome поддерживают его. IE, Safari и, возможно, другие нет.
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
На циклы in можно ссылаться только внутри этого цикла.
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
- имеет ограниченную область видимости и имеет ТДЗ. Однако есть две разные вещи.
Нет переназначения
Переменная, объявленная с использованием 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
В принятом ответе отсутствует точка:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
Проще говоря,
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
⚡️ Песочница для игры ↓
Вот пример различия между этими двумя (поддержка только началась для Chrome):
Как вы можете видеть var j
переменная все еще имеет значение вне области цикла for (Block Scope), но let i
переменная не определена вне области цикла 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);
Основным отличием является различие в объеме, хотя let может быть доступен только внутри объявленной области, как, например, в цикле for, к var можно получить доступ, например, вне цикла. Из документации в MDN (примеры также из MDN):
let позволяет вам объявлять переменные, которые ограничены в области действия блоком, оператором или выражением, в котором он используется. Это отличается от ключевого слова var, которое определяет переменную глобально или локально для всей функции независимо от области видимости блока.
Переменные, объявленные с помощью 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
При использовании внутри блока let ограничивает область действия переменной этим блоком. Обратите внимание на разницу между 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
scoping ведет себя больше как переменная scoping в более или менее любых других языках.
Например, он распространяется на вмещающий блок, они не существуют до их объявления и т. д.
Однако стоит отметить, что let
является лишь частью более новых реализаций Javascript и имеет различную степень поддержки браузера.
Переменная не поднимаетсяlet
не поднимет всю область блока, в котором они появляются.var
можно поднять, как показано ниже.{ console.log(cc); // undefined. Caused by hoisting var cc = 23; } { console.log(bb); // ReferenceError: bb is not defined let bb = 23; }
Вывоз мусора
Блок область действия
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
создайте новую лексическую среду с этими именами для а) выражения инициализатора б) каждой итерации (прежде всего для вычисления выражения приращения), более подробная информация здесь.
Разница заключается в объеме переменных, объявленных с каждой.
На практике существует ряд полезных последствий различия в области применения:
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
Вот пример, чтобы добавить к тому, что уже написали другие. Предположим, вы хотите сделать массив функций, 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.
Это объяснение было взято из статьи, которую я написал на Medium:
Поднятие — это механизм JavaScript, в котором объявления переменных и функций перемещаются в верхнюю часть области анализа синтаксическим анализатором, который считывает исходный код в промежуточное представление до того, как интерпретатор JavaScript начнет фактическое выполнение кода. Таким образом, не имеет значения, где объявлены переменные или функции, они будут перемещены в начало своей области действия независимо от того, является ли их область действия глобальной или локальной. Это означает, что
console.log (hi); var hi = "say hi";
на самом деле интерпретируется к этому
var hi = undefined; console.log (hi); hi = "say hi";
Итак, как мы только что видели, переменные поднимаются наверх своей области видимости и инициализируются со значением undefined, что означает, что мы можем на самом деле присвоить их значение до фактического объявления их в коде следующим образом:
hi = “say hi” console.log (hi); // say hi var hi; Regarding function declarations, we can invoke them before actually declaring them like so: sayHi(); // Hi function sayHi() { console.log('Hi'); }; Function expressions, on the other hand, are not hoisted, so we’ll get the following error: sayHi(); //Output: "TypeError: sayHi is not a function var sayHi = function() { console.log('Hi'); }; ES6 introduced JavaScript developers the `let` and `const` keywords. While `let` and `const` are block-scoped and not function
ограничено, так как это не должно иметь значения при обсуждении их поведения при подъеме. Мы начнем с конца, подъемники JavaScript и .
console.log(hi); // Output: Cannot access 'hi' before initialization let hi = 'Hi';
Как мы видим выше, не позволяет нам использовать необъявленные переменные, поэтому интерпретатор явно выводит ошибку ссылки, указывающую, что
hi
переменная не может быть доступна до инициализации. Та же ошибка произойдет, если мы изменим приведенное выше наconsole.log(hi); // Output: Cannot access 'hi' before initialization const hi = 'Hi';
Итак, суть в том, что синтаксический анализатор JavaScript ищет объявления переменных и функции и поднимает их наверх своей области видимости перед выполнением кода и присваивает им значения в памяти, поэтому, если интерпретатор столкнется с ними во время выполнения кода, он их распознает. и смогут выполнять код с присвоенными им значениями. Переменные, объявленные с помощью
let
или жеconst
остаются неинициализированными в начале выполнения, в то время как переменные, объявленные с помощьюvar
инициализируются со значениемundefined
.Я добавил эту наглядную иллюстрацию, чтобы помочь понять, как поднятые переменные и функции сохраняются в памяти.
Пусть следующие две функции показывают разницу:
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
}
ES6 представил два новых ключевых слова (let и const), заменяющие var.
Когда вам нужно замедление на уровне блока, вы можете использовать let и const вместо var.
В таблице ниже приведены различия между var, let и const.
Область действия функции VS:
Основное различие между 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
получить подъем:
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
объект (всегда тщательно продумывайте, если это действительно необходимо).
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].
Также кажется, что, по крайней мере, в Visual Studio 2015, TypeScript 1.5, "var" допускает несколько объявлений одного и того же имени переменной в блоке, а "let" - нет.
Это не сгенерирует ошибку компиляции:
var x = 1;
var x = 2;
Это будет:
let x = 1;
let x = 2;
var --> Function scope
let --> Block scope
const --> Block scope
вар
В этом примере кода переменная объявляется с помощьюvar
. Следовательно, у него есть область видимости функции . Это означает, что вы можете получить доступi
только изнутри. Вы не можете прочитать это снаружиfunction x
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
К счастью, его также можно использовать, чтобы избежать самовозвратных функций, используемых для имитации только приватных элементов - популярный шаблон проектирования, который снижает читаемость кода, усложняет отладку, не добавляет никакой реальной защиты кода или других преимуществ - за исключением, возможно, удовлетворения чьего-либо желания семантики, поэтому прекратите использовать Это. / напыщенная
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
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
:
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)
Пусть против вар. Все дело в сфере.
Переменные var являются глобальными и могут быть доступны практически везде, хотя пусть переменные не являются глобальными и существуют только до тех пор, пока закрывающая скобка не убьет их.
Посмотрите мой пример ниже и обратите внимание, как переменная lion (let) действует по-разному в двух console.logs; это выходит из области видимости во втором console.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.
Ниже показано, как 'let' и '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
, определяется let
изначально находится в глобальной области, а когда мы объявляемgfoo
снова внутри if clause
его область видимости изменилась, и когда новое значение присваивается переменной внутри этой области, это не влияет на глобальную область.
В то время как hfoo
, определяется var
изначально находится в глобальной области видимости, но снова, когда мы объявляем его внутриif clause
, он считает глобальную область видимости hfoo, хотя для ее объявления снова использовался var. И когда мы повторно назначаем его значение, мы видим, что глобальная область видимости hfoo также затронута. Это основное отличие.
Я только что наткнулся на один вариант использования, который мне пришлось использовать
var
над
let
ввести новую переменную. Вот такой случай:
Я хочу создать новую переменную с динамическими именами переменных.
let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a); // this doesn't work
let variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a); // this works
Приведенный выше код не работает, потому что
eval
вводит новый блок кода. Декларация с использованием
var
объявит переменную вне этого блока кода, поскольку
var
объявляет переменную в области видимости функции.
let
, с другой стороны, объявляет переменную в области видимости блока. Так,
a
переменная будет видна только в
eval
блок.
Пусть является частью 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
}
Как уже упоминалось выше:
Разница в том,
var
ограничен до ближайшего функционального блока иlet
ограничен ближайшим включающим блоком, который может быть меньше функционального блока. Оба являются глобальными, если находятся вне какого-либо блока. Давайте посмотрим на пример:
Example1:
В обоих моих примерах у меня есть функция myfunc
, myfunc
содержит переменную myvar
равно 10. В моем первом примере я проверяю, myvar
равно 10 (myvar==10
) Если да, я объявляю переменную myvar
(теперь у меня есть две переменные myvar) с помощью var
ключевое слово и присвойте ему новое значение (20). В следующей строке я печатаю его значение на моей консоли. После условного блока я снова печатаю значение myvar
на моей консоли. Если вы посмотрите на вывод myfunc
, myvar
имеет значение, равное 20.
Пример 2: во втором примере вместо использования var
Ключевое слово в моем условном блоке я объявляю myvar
с помощью let
ключевое слово Теперь, когда я звоню myfunc
Я получаю два разных вывода: myvar=20
а также myvar=10
,
Таким образом, разница очень проста, т.е.
Теперь я думаю, что есть лучшая область видимости переменных для блока операторов, используя 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
}
Я думаю, что люди начнут использовать let here после этого, чтобы у них была такая же область видимости в JavaScript, как и в других языках, Java, C# и т. Д.
Люди с неясным пониманием области видимости в JavaScript раньше делали ошибку.
Подъем не поддерживается с помощью let
,
При таком подходе ошибки, присутствующие в JavaScript, удаляются.
Обратитесь к ES6 In глубине: позвольте и const, чтобы понять это лучше.