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.
Взгляните на этот пример из другого вопроса 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
известна только в первом цикле 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 подняты .Это означает, что объявления всегда находятся на вершине области видимости.
(**) Переменные области видимости не подняты
i
IS известен везде в функциональном блоке! Он начинается как undefined
(из-за подъема), пока вы не назначите значение! ps: let
также поднимается (до верхней части содержащего его блока), но будет давать ReferenceError
ссылку, когда на него ссылаются в блоке перед первым присваиванием. (ps2: я парень вроде точки с запятой, но вам не нужна точка с запятой после блока). При этом, спасибо за добавление проверки реальности в отношении поддержки!
let
и var
!
let
и const
его рекомендовали использовать только тогда, когда вам действительно нужны их дополнительные функции , потому что принудительное выполнение / проверка этих дополнительных функций (таких как 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
. Upvoted.
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
блок-область и имеет 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 и имеет различную степень поддержки браузера .
let
включен в черновик 6-го издания и, скорее всего, будет в окончательной спецификации.
let
. Safari, IE и Chome - нет.
let
не поднимайте, чтобы использовать переменную, let
определенную в верхней части вашего блока. Если у вас есть if
утверждение, которое состоит из нескольких строк кода, вы можете забыть, что не можете использовать эту переменную до тех пор, пока она не будет определена. БОЛЬШАЯ ТОЧКА !!!
let
будет поднята переменная в верхнюю часть блока. Однако ссылка на переменную в блоке до объявления переменной приводит к ReferenceError (мое примечание: вместо старого доброго undefined
). переменная находится в «временной мертвой зоне» от начала блока до обработки объявления. " То же самое касается «операторов переключения, потому что есть только один базовый блок». Источник: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Основное отличие является сфера разницы, в то время как пусть может быть доступны только внутри сферы он объявлен, как в течение цикла, вар может быть доступен за пределами цикла, например. Из документации в 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
При использовании внутри блока 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'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 is $ {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
переменная bar, объявленная с помощью 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
объекте (всегда тщательно продумывайте, действительно ли это необходимо).
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
для каждой итерации цикла переменная находится в области видимости и имеет общую копию.
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
⚡️ Песочница для игры ↓
Если я читаю спецификации правильно, то, к 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
вашем «классе» есть только один экземпляр.
Некоторые хаки с 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
.
пусть является частью 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 глобальны и доступны практически везде, а переменные пусть не глобальны и существуют только до тех пор, пока закрывающая скобка не убьет их.
Посмотрите мой пример ниже и обратите внимание, как переменная 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.
ES6 представил два новых ключевых слова ( let и const ), альтернативных var .
Когда вам нужно замедление на уровне блоков, вы можете использовать let и const вместо var.
В таблице ниже приведены различия между var, let и const
Ниже показано, как «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
ограничен до ближайшего окружающего блока , который может быть меньше функционального блока. Оба являются глобальными, если находятся вне какого-либо блока. Давайте посмотрим на пример:
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
.
Таким образом, разница очень проста, т.е.
Я хочу связать эти ключевые слова с контекстом выполнения, потому что контекст выполнения важен во всем этом. Контекст выполнения имеет две фазы: фаза создания и фаза выполнения. Кроме того, каждый контекст выполнения имеет переменную среду и внешнюю среду (свою лексическую среду).
На этапе создания контекста выполнения var, let и const будут по-прежнему сохранять свою переменную в памяти с неопределенным значением в среде переменных данного контекста выполнения. Разница заключается в фазе исполнения. Если вы используете ссылку на переменную, определенную с помощью var, до того, как ей будет присвоено значение, она будет просто неопределенной. Никаких исключений не будет.
Однако вы не можете ссылаться на переменную, объявленную с помощью let или const, пока она не будет объявлена. Если вы попытаетесь использовать его до того, как он будет объявлен, тогда будет возникать исключительная ситуация на этапе выполнения контекста выполнения. Теперь переменная все еще будет в памяти, благодаря фазе создания контекста выполнения, но механизм не позволит вам использовать ее:
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
С переменной, определенной с помощью var, если Engine не может найти переменную в переменной среде текущего контекста выполнения, он поднимется по цепочке областей действия (внешняя среда) и проверит переменную среду внешней среды для переменной. Если он не может найти его там, он продолжит поиск в Scope Chain. Это не относится к let и const.
Вторая особенность let - это введение в область видимости блока. Блоки определяются фигурными скобками. Примеры включают в себя функциональные блоки, блоки if, для блоков и т. Д. Когда вы объявляете переменную с помощью let inside блока, переменная доступна только внутри блока. Фактически, каждый раз, когда блок запускается, например, в цикле for, он создает новую переменную в памяти.
ES6 также вводит ключевое слово const для объявления переменных. const также имеет ограниченную область видимости. Разница между let и const заключается в том, что переменные const должны быть объявлены с использованием инициализатора, иначе это вызовет ошибку.
И, наконец, когда дело доходит до контекста выполнения, переменные, определенные с помощью var, будут присоединены к объекту 'this'. В глобальном контексте выполнения это будет объект окна в браузерах. Это не относится к let или 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
}
Я думаю, что люди начнут использовать let here после этого, чтобы у них была такая же область видимости в JavaScript, как и в других языках, Java, C # и т. Д.
Люди с неясным пониманием области видимости в JavaScript раньше делали ошибку.
Подъем не поддерживается с помощью let
.
При таком подходе ошибки, присутствующие в JavaScript, удаляются.
Обратитесь к ES6 In Depth: пусть и const, чтобы понять это лучше.
Эта статья четко определяет разницу между var, let и const
const
является сигналом того, что идентификатор не будет переназначен.
let
, является сигналом о том, что переменная может быть переназначена, например, счетчик в цикле или обмен значениями в алгоритме. Это также сигнализирует о том, что переменная будет использоваться только в блоке, в котором она определена, что не всегда является всей содержащей функцией.
var
теперь является самым слабым сигналом, доступным при определении переменной в JavaScript. Переменная может быть или не быть переназначена, и переменная может или не может использоваться для всей функции или просто для цели блока или цикла.
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
включен в черновик 6-го издания и, скорее всего, будет в окончательной спецификации.