ECMAScript 6 представил оператор let
.
Я слышал, что она описывается как "локальная" переменная, но я все еще не совсем уверен, как она ведет себя иначе, чем ключевое слово var
.
Какие есть отличия? Когда следует let
использовать над var
?
ECMAScript 6 представил оператор let
.
Я слышал, что она описывается как "локальная" переменная, но я все еще не совсем уверен, как она ведет себя иначе, чем ключевое слово 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.
Взгляните на этот пример из fooobar.com/questions/166/...:
var funcs = [];
// let 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 run each one to see
funcs[j]();
}
My value: 3
выводился на консоль каждый раз, когда вызывался funcs[j]();
, поскольку анонимные функции были связаны с одной и той же переменной.
Людям приходилось создавать функции, вызываемые немедленно, чтобы получить правильные значения из циклов, но это также было проблематично.
В то время как переменные, объявленные с ключевым словом var
, "подняты" в верхнюю часть блока, что означает, что они доступны в своей области охвата даже до того, как они объявлены:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
пусть переменные не инициализируются, пока их определение не будет оценено. Доступ к ним до инициализации приводит к 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
также может использоваться, чтобы избежать проблем с затворами. Он связывает новое значение, а не сохраняет старую ссылку, как показано в примерах ниже.
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>
<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
и 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 или babel-standalone), вы можете смело использовать оператор let
, однако ваш код скорее всего будет не оптимальным с точки зрения производительности.
Если вы пишете код JavaScript на стороне клиента и используете транспортер на основе Node (например, скрипт оболочки traceur или Babel), вы можете смело использовать оператор let
. А поскольку ваш браузер будет знать только о переданном коде, недостатки производительности должны быть ограничены.
Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.
Есть еще некоторые браузеры, которые вообще не поддерживают let
:
Актуальный обзор того, какие браузеры поддерживают оператор let
на момент прочтения этого ответа, см. на этой странице Can I Use
.
* Глобальные и функциональные переменные могут быть инициализированы и использованы до их объявления, поскольку переменные JavaScript подняты. Это означает, что объявления всегда находятся на вершине области видимости.
** Переменные области видимости не отображаются
Вот объяснение ключевого слова let
с некоторыми примерами.
let
работает очень похоже наvar
. Основное различие заключается в том, что область действия переменнойvar
- это вся включающая функция
Эта таблица в Википедии показывает, какие браузеры поддерживают Javascript 1.7.
Обратите внимание, что только браузеры Mozilla и Chrome поддерживают его. IE, Safari и, возможно, другие не делают.
В принятом ответе отсутствует точка:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
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
-it в блочном пространстве и имеет 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
по-прежнему имеет значение за пределами области действия цикла 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
видимости ведет себя больше как переменная область видимости в более или менее любых других языках.
Например, он распространяется на вмещающий блок, они не существуют до их объявления и т.д.
Однако стоит отметить, что let
является лишь частью более новых реализаций Javascript и имеет различную степень поддержки браузера.
Основное отличие заключается в разнице scope, в то время как let может быть доступен только внутри объявленной scope, как в цикле 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
}'
На верхнем уровне программ и функций let, в отличие от 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
будет не поднимать во всю область блока, в котором они появляются. Напротив, var
может подниматься, как показано ниже.
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
Собственно, Per @Bergi, Поднимаются как 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
создает новую лексическую среду с этими именами для a) выражение инициализатора; 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 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 scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined
На этот раз i
восстанавливается на каждой итерации цикла for
. Каждая функция теперь сохраняет значение i
во время создания функции, а adderFunctions
ведет себя как ожидалось.
Теперь изображение смешивает два поведения, и вы, вероятно, увидите, почему не рекомендуется смешивать новые let
и const
со старыми var
в том же script. Это может привести к некоторому эффектно запутанному коду.
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 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
Не позволяйте этому случиться с вами. Используйте linter.
ПРИМЕЧАНИЕ.. Это пример обучения, призванный продемонстрировать поведение
var
/let
в циклах и закрытие функций, которые также будут легко понятны. Это был бы ужасный способ добавить числа. Но общая техника захвата данных при закрытии анонимных функций может встречаться в реальном мире в других контекстах. YMMV.
Разница заключается в scope переменных, объявленных с каждым.
На практике существует ряд полезных последствий разницы в области:
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 5" пять раз:
i is 5
i is 5
i is 5
i is 5
i is 5
Могут отображаться следующие две функции:
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
получает testVar
переменную foo, объявленную с помощью var
, все еще доступна вне оператора if
. Эта переменная foo
будет доступна везде в рамках функции testVar
.
переменные с let
:
Когда вторая функция testLet
переменная bar, объявленная с let
, доступна только внутри оператора if
. Поскольку переменные, объявленные с let
являются блочными (где блок является кодом между фигурными скобками, например if{}
, for{}
, function{}
).
let
переменные не поднимаются: Другое различие между var
и let
- переменные с объявленными, и let
их не поднимают. Пример - лучший способ проиллюстрировать это поведение:
переменные с let
not let
hoisted:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
переменные с var
do get hoist:
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
over var
всякий раз, когда вы можете, потому что он просто ограничен более конкретным. Это уменьшает потенциальные конфликты именования, которые могут возникать при работе с большим количеством переменных. var
можно использовать, если вы хотите, чтобы глобальная переменная явно была на объекте window
(всегда внимательно изучите, если это действительно необходимо).
Также представляется, что, по крайней мере, в Visual Studio 2015, TypeScript 1.5, "var" допускает несколько объявлений с одним и тем же именем переменной в блоке, а "let" - нет.
Это не приведет к ошибке компиляции:
var x = 1;
var x = 2;
Это будет:
let x = 1;
let x = 2;
Если я читаю спецификацию прямо тогда 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
Смотрите " Эмуляция частных интерфейсов ".
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
присоединяет объявление переменной к какому-либо блоку (обычно a { .. }
), в котором он содержится. Другими словами, 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,7, то вы счастливый парень.
Некоторые хаки с 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 является частью 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
}
Раньше в JavaScript были только две области, то есть функциональные и глобальные. С ключевым словом "let
" JavaScript теперь ввел переменные block-level
.
Чтобы иметь полное понимание ключевого слова 'let', ES6: 'let ключевое слово для объявления переменной в JavaScript поможет.
Теперь я думаю, что лучше использовать область видимости переменных для блока операторов с помощью 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 after, чтобы они имели аналогичную область видимости в JavaScript, как и другие языки, Java, С# и т.д.
Люди, не имеющие четкого представления о области видимости в JavaScript, используемые для совершения ошибки ранее.
Подъем не поддерживается с помощью let
.
При таком подходе ошибки, возникающие в JavaScript, удаляются.
Обратитесь к ES6 In Depth: let и const, чтобы лучше понять его.
В этой статье четко определена разница между var, let и const
const
- это сигнал, что идентификатор не будет переназначен.
let
, является сигналом, что переменная может быть переназначена, например, счетчик в цикле или обмен значениями в алгоритме. Он также сигнализирует что переменная будет использоваться только в блоке, в котором она определена, которая не всегда является целой содержащей функцией.
var
теперь является самым слабым сигналом, доступным при определении переменной в JavaScript. Переменная может быть переназначена или не может быть переназначена, а переменная может использоваться или не использоваться для целой функции или просто для назначение блока или цикла.
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
Как уже упоминалось выше:
Разница заключается в определении области.
var
распространяется до ближайшего функционального блока, иlet
распространяются до ближайшего заключающего блока, который может быть меньше, чем функциональный блок. Оба являются глобальными, если вне любого блока. Давайте посмотрим пример:
Example1:
В обоих моих примерах у меня есть функция myfunc
. myfunc
содержит переменную myvar
10. В моем первом примере я проверяю, равен ли myvar
10 (myvar==10
). Если да, я agian объявляю переменную myvar
(теперь у меня есть две переменные myvar), используя ключевое слово var
и присваиваю ему новое значение (20). В следующей строке я напечатаю его значение на моей консоли. После условного блока я снова распечатываю значение myvar
на моей консоли. Если вы посмотрите на выход myfunc
, myvar
имеет значение равное 20.
Пример2: В моем втором примере вместо использования ключевого слова var
в моем условном блоке объявляю myvar
используя ключевое слово let
. Теперь, когда я вызываю myfunc
я получаю два разных выхода: myvar=20
и myvar=10
.
Таким образом, разница очень проста, т.е. ее объем.
Посмотрите на это изображение, я создал один очень простой пример для демонстрации переменных const
и let
. Как вы можете видеть, когда вы пытаетесь изменить переменную const
, вы получите ошибку (Попытка переопределить "имя", которая является константой), но посмотрите на переменную let
...
Сначала мы объявляем let age = 33
, а затем присваиваем какое-то другое значение age = 34;
, что нормально, у нас нет никаких ошибок, когда мы пытаемся изменить переменную let
Поскольку в настоящее время я пытаюсь получить глубокое понимание JavaScript, я поделюсь своим кратким исследованием, которое содержит некоторые из уже обсужденных замечательных частей, а также некоторые другие детали в другой перспективе.
Понимание разницы между var и let может быть проще, если мы понимаем разницу между функцией и областью действия блока.
Давайте рассмотрим следующие случаи:
(function timer() {
for(var i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack VariableEnvironment //one VariablEnvironment for timer();
// when the timer is out - the value will be the same value for each call
5. [setTimeout, i] [i=5]
4. [setTimeout, i]
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]
####################
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i] [i=5]
LexicalEnvironment
4. [setTimeout, i] [i=4]
LexicalEnvironment
3. [setTimeout, i] [i=3]
LexicalEnvironment
2. [setTimeout, i] [i=2]
LexicalEnvironment
1. [setTimeout, i] [i=1]
LexicalEnvironment
0. [setTimeout, i] [i=0]
когда timer()
вызывается, создается ExecutionContext, который будет содержать как VariableEnvironment, так и все LexicalEnvironments, соответствующие каждой итерации.
И более простой пример
Область действия функции
function test() {
for(var z = 0; z < 69; z++) {
//todo
}
//z is visible outside the loop
}
Блок Сфера
function test() {
for(let z = 0; z < 69; z++) {
//todo
}
//z is not defined :(
}
Я думаю, что термины и большинство примеров немного ошеломляют. Основная проблема, с которой я столкнулся лично, - это понимание, что такое "Блок". В какой-то момент я понял, что блоком будут любые фигурные скобки, кроме оператора 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);