В чем разница между использованием let и var?

В ECMAScript 6 представлен оператор let.

Я слышал, что это описывается как local переменная, но я до сих пор не совсем уверен, как она ведет себя иначе, чем ключевое слово var.

Какие отличия ?. Когда следует использовать let вместо var?


person TM.    schedule 17.04.2009    source источник
comment
ECMAScript является стандартом, и let включен в черновик 6-го издания и, скорее всего, будет в окончательной спецификации.   -  person Richard Ayotte    schedule 31.03.2012
comment
См. kangax.github.io/es5-compat-table/es6 для актуальная матрица поддержки функций ES6 (включая let). На момент написания Firefox, Chrome и IE11 все его поддерживают (хотя я считаю, что реализация FF не совсем стандартная).   -  person Nico Burns    schedule 17.01.2014
comment
Долгое время я не знал, что переменные в цикле for ограничены функцией, в которую они заключены. Я помню, как впервые понял это и подумал, что это очень глупо. Я действительно вижу некоторую мощь, хотя теперь знаю, как эти два могут использоваться f по разным причинам и как в некоторых случаях вы можете действительно захотеть использовать var в цикле for, а не привязать его к блоку.   -  person Eric Bishard    schedule 07.05.2015
comment
По мере улучшения поддержки функций ES6 вопрос о внедрении ES6 смещается с поддержки функций на различия в производительности. Таким образом, вот сайт, на котором я обнаружил различия в производительности между ES6 и ES5. Имейте в виду, что со временем это, вероятно, изменится, поскольку движки оптимизируются для кода ES6.   -  person timolawl    schedule 04.05.2016
comment
Это очень хорошее чтение. wesbos.com/javascript-scoping   -  person onmyway133    schedule 15.06.2017


Ответы (39)


Правила определения объема

Основное отличие - это правила определения объема. Переменные, объявленные ключевым словом var, относятся к непосредственному телу функции (отсюда и к области действия функции), в то время как let переменные ограничиваются непосредственным охватывающим блоком, обозначенным { } (отсюда и область действия блока).

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

Причина, по которой ключевое слово let было введено в язык, заключалась в том, что область действия функции сбивала с толку и была одним из основных источников ошибок в JavaScript.

Взгляните на этот пример из другого вопроса о переполнении стека:

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"; // not allowed to be 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, 'foo1' is replaced with 'foo2'.

let bar = "bar1"; 
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

person Community    schedule 12.07.2012
comment
Помните, что вы можете создать блок, когда захотите. function () {код; {пусть inBlock = 5; } код; }; - person average Joe; 14.12.2012
comment
Итак, цель операторов let - только освободить память, когда она не нужна в определенном блоке? - person NoBugs; 07.06.2013
comment
@NoBugs, Да, и рекомендуется, чтобы переменные существовали только там, где они нужны. - person batman; 07.06.2013
comment
let не поднимается, как его аналог 'var'. Подъем работает по-разному вокруг функционального блока и любого другого блока. - person Anvesh Checka; 26.09.2014
comment
let выражение блока let (variable declaration) statement является нестандартным и будет удалено в будущем, bugzilla.mozilla.org /show_bug.cgi?id=1023609. - person Gajus; 17.12.2014
comment
Хотя включающий блок let устарел, вы можете сделать то же самое, создав явный блок с фигурными скобками. {let bar = foo; пусть foo = bar; code; more code; console.log (bar + foo); } и просто поместите ваши let в верхней части блока кода в фигурные скобки. - person Eric Bishard; 07.05.2015
comment
Только что нашел это репо, которое позволяет вам использовать блоки let в вашем коде. ИМХО, потому что let-блоки довольно хороши. github.com/getify/let-er, который Transpiles non-ES6 let-blocks into ES6 (or ES3) - person Eric Bishard; 07.05.2015
comment
есть языки, в которых вы можете использовать одиночные фигурные скобки для создания блока (и, вероятно, области видимости тоже), это работает или планируется и для js? особенно, что let (variableName = value) {block} устарело - person zakius; 16.09.2015
comment
Примечание. Как сказал @EricB, вы можете использовать let a = 1; { let a = 2; console.log(a === 2) } console.log(a === 1) вместо let a = 1; let (a = 2) { console.log(a === 2) } console.log(a === 1). Он делает то же самое. - person Toothbrush; 23.10.2015
comment
Итак, я просто не могу придумать ни одного случая, когда использование var было бы полезно. Может ли кто-нибудь привести мне пример ситуации, когда предпочтительнее использовать var? - person Luis Sieira; 08.11.2015
comment
let на верхнем уровне не идентичен var - let явно не будет создавать глобальные ссылки: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ - person Dave Newton; 01.04.2016
comment
К сожалению, браузеры по-прежнему (по состоянию на май 2017 г.) не оптимизируют все угловые случаи для этой замечательной функции, и из-за этого возможны некоторые замедления цикла stackoverflow.com/questions/43153889/ - person Chajnik-U; 08.05.2017
comment
Этот ответ не затрагивает часть вопроса: когда следует использовать let вместо var ?. Я попытался ответить на этот вопрос, а также собрал много полезного материала в других ответах, ниже. Если какая-то добрая душа пожелает, чтобы это было помещено в вики сообщества, я не буду возражать против полного или частичного копирования и вставки. - person mormegil; 22.05.2017
comment
Я наблюдаю, что поведение повторного объявления остается таким же даже без режима 'use strict';. Таким образом, кажется, что повторное объявление переменной с тем же именем с использованием ключевого слова let всегда приведет к ошибке, независимо от того, используем ли мы строгий режим или нет. Я использую хром версии 58. - person RBT; 01.06.2017
comment
Я хотел бы исправить, что let me = 'go' на самом деле не находится в глобальной области видимости, например var me = 'go', если вы попробуете this.me с let, вы получите undefined. Если вы попробуете this.me с var, вы получите значение me, в данном случае go. - person Tim Hong; 23.04.2018
comment
Я думаю, что «немедленно» было бы лучше, чем «ближайший» в ближайшем функциональном блоке. - person snnsnn; 11.09.2019
comment
Используйте let, поскольку это более предсказуемое поведение, что приводит к меньшему количеству проблем. Но если ваше приложение должно работать с более старыми устройствами, возможно, оно не будет поддерживать ES6, который включает let. Я бы сказал - это зависит от варианта использования. - person Jakub Kubista; 03.06.2020
comment
Также обратите внимание, что "use strict" предотвратит определение необъявленных переменных как глобальных. var health = 5; halth = 1 приведет к ошибке ссылки в отличие от опечатки var halth, определенной в глобальной области видимости. - person Hydra; 18.07.2021

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>

person Gurpreet Singh    schedule 27.05.2015
comment
Это действительно круто. Я ожидаю, что i будет определен вне тела цикла, заключенного в скобки, и НЕ будет замыкать i. Конечно, ваш пример доказывает обратное. Я думаю, что это немного сбивает с толку с точки зрения синтаксиса, но этот сценарий настолько распространен, что имеет смысл поддерживать его таким образом. Большое спасибо за то, что подняли этот вопрос. - person Karol Kolenda; 27.07.2015
comment
IE 11 поддерживает let, но предупреждает 6 для всех кнопок. Есть ли у вас источник, говорящий о том, как let должен себя вести? - person Jim Hunziker; 22.10.2015
comment
Похоже, ваш ответ - правильное поведение: developer.mozilla .org / en-US / docs / Web / JavaScript / Reference / - person Jim Hunziker; 22.10.2015
comment
Читая ссылку в комментарии @Jim, я заметил заметку, в которой упоминалось, что пусть работает в Firefox 44. - person Marie; 28.01.2016
comment
На самом деле это распространенная ошибка в Javascript, и теперь я понимаю, почему let был бы действительно полезен. Установка прослушивателей событий в цикле больше не требует немедленного вызова выражения функции для локального определения i на каждой итерации. - person Adrian Moisa; 21.02.2016
comment
Использование let лишь решает эту проблему. Таким образом, каждая итерация создает частную независимую область видимости блока, но переменная i все еще может быть повреждена последующими изменениями внутри блока (при условии, что переменная итератора не обычно изменяется внутри блока, но другие объявленные переменные let внутри блока вполне может быть), и любая функция, объявленная в блоке, может при вызове повредить значение i для других функций, объявленных в блоке, потому что они действительно используют одну и ту же область частного блока, следовательно, одинаковую ссылка на i. - person gary; 08.09.2016
comment
@gary Согласно MDN + изменение приведенной выше демонстрации на i++, каждая функция имеет свою собственную область действия блока и не мешает другим экземплярам: jsfiddle.net/rmXcF/474 - person krulik; 20.03.2017
comment
@krulik Вот что я имел в виду: jsfiddle.net/k7nmhoed две функции, объявленные в одном блоке, изменяют общий переменная цикла i. - person gary; 31.03.2017
comment
for(let i = 1; i < 6; i++) Лично я считаю, что это обозначение вводит в заблуждение: i++ предполагает, что i увеличивается на 1. Вместо этого новая переменная с именем i передается в следующую итерацию и инициализирует значение i в предыдущей итерации, увеличенное на 1. Думаю, им следовало выбрать другое обозначение. - person giorgio-b; 08.03.2018
comment
Для читателей Javascript: The Good Parts обратите внимание, что использование let вместо var в цикле for (как во втором блоке кода в этом ответе) предотвращает отображение кода предупреждения, содержащего только количество узлов; вот ссылка на пример кода. - person jrh; 26.12.2018
comment
Эээ ... Я получаю 12345 для обоих примеров ... FF 69.0 - person Andrew; 09.09.2019
comment
@gary - это именно то, что должно произойти. i доступен для всего вложенного кода. Без этого нет простого способа ссылаться на переменные во внешних областях видимости. Если этим двум функциям нужны независимые is, то каждая из них объявляет свои, используя let. OTOH Я понимаю вашу точку зрения - нет способа гарантировать, что i заморожен (постоянен) внутри цикла. Можно назначить его константе: const j = i;, но все же придется вручную проверять, что на i нигде нет ссылок. - person ToolmakerSteve; 15.10.2019
comment
Что-то изменилось в Javascript с тех пор, как был опубликован этот ответ? И var, и let version дают одинаковые результаты. - person Kirby L. Wallace; 23.10.2019
comment
@ KirbyL.Wallace и другие запутались в одном и том же результате для обоих примеров: я считаю, что разница в том, что происходит, когда вы нажимаете каждое число, а не в том, что выводится. В обоих примерах на экране должны отображаться 1,2,3,4,5, но в консоли они должны приводить к тому, что при щелчке каждого из них отображаются разные значения по сравнению с другим. - person TylerH; 13.04.2021

В чем разница между let и var?

  • Переменная, определенная с помощью оператора var, известна в функции он определяется в с самого начала функции. (*)
  • Переменная, определенная с помощью оператора let, известна только в block, в котором он определен, с момента его определения и далее. (**)

Чтобы понять разницу, рассмотрим следующий код:

// 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 на стороне клиента и используете транспилятор на основе узла (например, сценарий оболочки traceur или Babel), вы можно безопасно использовать оператор let. А поскольку ваш браузер будет знать только о перенесенном коде, недостатки производительности должны быть ограничены.

  • Если вы пишете код JavaScript на стороне клиента и не используете транспилятор, вам необходимо рассмотреть возможность поддержки браузером.

    Некоторые браузеры по-прежнему не поддерживают let:

введите описание изображения здесь


Как отслеживать поддержку браузера

Актуальный обзор того, какие браузеры поддерживают оператор let на момент чтения этого ответа, см. На странице < strong> эта Can I Use страница.


(*) Переменные с глобальной и функциональной областью видимости можно инициализировать и использовать до того, как они будут объявлены, поскольку переменные JavaScript поднял. Это означает, что объявления всегда находятся в верхней части области видимости.

(**) Переменные с областью действия блока не поднимаются

person John Slegers    schedule 23.02.2016
comment
Относительно ответа v4: i В функциональном блоке известно везде! Он начинается как undefined (из-за подъема), пока вы не присвоите значение! ps: let также поднимается (в верхнюю часть содержащего его блока), но перед первым назначением будет выдавать ReferenceError при ссылке в блоке. (ps2: я вроде как сторонник точки с запятой, но вам действительно не нужна точка с запятой после блока). При этом спасибо за добавление проверки реальности в отношении поддержки! - person GitaarLAB; 21.05.2016
comment
@GitaarLAB: Согласно сети разработчиков Mozilla: В ECMAScript 2015 привязки let не подлежат поднятию переменных, что означает, что объявления let не перемещаются в начало текущего контекста выполнения. - Во всяком случае, я внес несколько улучшений в свой ответ, которые должны прояснить разницу в поведении при подъеме между let и var! - person John Slegers; 27.02.2018
comment
Ваш ответ сильно улучшился (я тщательно проверял). Обратите внимание, что та же ссылка, на которую вы ссылались в своем комментарии, также говорит: Переменная (let) находится во временной мертвой зоне от начала блока до обработки инициализации. Это означает, что «идентификатор» (текстовая строка, «зарезервированная» для указания на «что-то») уже зарезервирована в соответствующей области, иначе она стала бы частью области корня / хоста / окна. . Лично для меня «подъем» означает не что иное, как резервирование / привязку объявленных «идентификаторов» к их соответствующей области действия; исключая их инициализацию / присвоение / модифицируемость! - person GitaarLAB; 01.03.2018
comment
И .. +1. Статья Кайла Симпсона, на которую вы ссылаетесь, прочитана отлично, спасибо вам за это! Также ясно и о временной мертвой зоне, известной как TDZ. Я хотел бы добавить одну интересную вещь: я читал в MDN, что let и const рекомендуется использовать только тогда, когда вам действительно нужны их дополнительные функции, потому что принудительное выполнение / проверка этих дополнительных функций (например, записи -only const) приводит к «дополнительной работе» (и дополнительным узлам области видимости в дереве областей видимости) для (текущего) механизма (ов) для принудительного выполнения / проверки / проверки / настройки. - person GitaarLAB; 01.03.2018
comment
Обратите внимание, что MDN говорит, что IE правильно интерпретирует let. Что он? developer.mozilla.org/en-US/docs/ Интернет / JavaScript / Справочник / - person Katinka Hesselink; 06.02.2019
comment
@KatinkaHesselink: Я не вижу, чтобы MDN говорил что-то подобное. Насколько я могу судить, они просто не учли причуду IE11, которую другие правильно задокументировали. См. Также stackoverflow.com/questions / 2356830 /. - person John Slegers; 05.04.2019

Вот объяснение ключевого слова let с некоторыми примерами.

let работает очень похоже на var. Основное отличие состоит в том, что область видимости переменной var - это вся включающая функция.

Эта таблица в Википедии показывает, какие браузеры поддерживают Javascript 1.7.

Обратите внимание, что его поддерживают только браузеры Mozilla и Chrome. IE, Safari и, возможно, другие этого не делают.

person Ben S    schedule 17.04.2009
comment
Ключевой фрагмент текста из связанного документа, похоже, работает так же, как var. Основное отличие состоит в том, что область видимости переменной var - это вся включающая функция. - person Michael Burr; 18.04.2009
comment
@olliej, на самом деле Mozilla идет впереди всех. См. Страницу 19 в ecma-international.org/publications/ files / ECMA-ST / Ecma-262.pdf - person Tyler Crompton; 19.06.2012
comment
@TylerCrompton, это всего лишь набор слов, зарезервированных годами. Когда была добавлена ​​Mozilla, пусть это было просто расширение Mozilla без соответствующей спецификации. ES6 должен определять поведение для операторов let, но это произошло после того, как mozilla представила синтаксис. Помните, что у moz также есть E4X, который полностью мертв и только moz. - person olliej; 11.07.2012
comment
В IE11 добавлена ​​поддержка let msdn.microsoft. .com / en-us / library / ie / dn342892% 28v = vs.85% 29.aspx. - person eloyesp; 24.12.2013
comment
Теперь let поддерживает все современные браузеры, кроме Opera, Blackberry и QQ. - person Shapon Pal; 08.01.2019

В принятом ответе отсутствует точка:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
person Lcf.vs    schedule 02.06.2015
comment
Принятый ответ НЕ объясняет этот момент в своем примере. Принятый ответ продемонстрировал это только в инициализаторе цикла for, резко сужая область применения ограничений let. Проголосовали. - person Jon Davis; 22.09.2015
comment
@ stimpy77 Он явно указывает, что let ограничена ближайшим охватывающим блоком; нужно ли включать все способы проявления? - person Dave Newton; 01.04.2016
comment
было много примеров, и ни один из них не продемонстрировал должным образом ... Возможно, я поддержал и принятый ответ, и этот? - person Jon Davis; 01.04.2016
comment
Этот вклад демонстрирует, что блок может быть просто набором строк, заключенных в скобки; то есть его не нужно связывать с каким-либо потоком управления, циклом и т. д. - person webelo; 22.11.2017
comment
Стоит +9999, но я не могу этого дать ... - person Logan Devine; 28.05.2021

let

Область действия блока

Переменные, объявленные с использованием ключевого слова let, имеют блочную область видимости, что означает, что они доступны только в блок, в котором они были объявлены.

На верхнем уровне (вне функции)

На верхнем уровне переменные, объявленные с использованием let, не создают свойств глобального объекта.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

Внутри функции

Внутри функции (но вне блока) let имеет ту же область видимости, что и var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Внутри блока

Переменные, объявленные с использованием let внутри блока, недоступны за пределами этого блока.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Внутри петли

На переменные, объявленные с let в циклах, можно ссылаться только внутри этого цикла.

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Петли с закрытием

Если вы используете let вместо var в цикле, с каждой итерацией вы получаете новую переменную. Это означает, что вы можете безопасно использовать замыкание внутри цикла.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Временная мертвая зона

Из-за временной мертвой зоны переменные, объявленные с использованием let, не могут быть доступны до их объявления. Попытка сделать это вызывает ошибку.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

Нет повторного объявления

Вы не можете объявить одну и ту же переменную несколько раз, используя let. Вы также не можете объявить переменную с использованием let с тем же идентификатором, что и другая переменная, которая была объявлена ​​с использованием var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

const очень похож на let - он блочный и имеет TDZ. Однако есть две разные вещи.

Нет переназначения

Переменная, объявленная с использованием const, не может быть переназначена.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Обратите внимание, что это не означает, что значение неизменяемо. Его свойства еще можно изменить.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

Если вы хотите иметь неизменяемый объект, вы должны использовать Object.freeze().

Требуется инициализатор

Вы всегда должны указывать значение при объявлении переменной с помощью const.

const a; // SyntaxError: Missing initializer in const declaration
person Michał Perłakowski    schedule 23.11.2016
comment
Это очень ясное объяснение декларации в JS ... И вдруг я понял, где моя проблема в одном из for циклов: - | - person 1000Gbps; 07.04.2021

Проще говоря,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ Песочница для игры

 Изменить let vs var

person Hasan Sefa Ozalp    schedule 11.05.2020

Вот пример разницы между ними (поддержка Chrome только началась):
введите здесь описание изображения

Как вы можете видеть, переменная var j по-прежнему имеет значение за пределами области цикла for (область действия блока), но переменная 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);

person vlio20    schedule 06.03.2015
comment
Какой инструмент я здесь ищу? - person Barton; 25.03.2015
comment
Как разработчик настольных апплетов для Cinnamon, я не сталкивался с такими замечательными инструментами. - person Barton; 26.10.2017

Основное отличие заключается в разнице области, тогда как let может быть доступна только в объявленной области, например в цикле for, var можно получить, например, вне цикла. Из документации в MDN (примеры также из MDN):

let позволяет объявлять переменные, область действия которых ограничена блоком, оператором или выражением, в которых они используются. В этом отличие от ключевого слова var, которое определяет переменную глобально или локально для всей функции независимо от области действия блока.

Переменные, объявленные с помощью let, имеют в качестве своей области действия блок, в котором они определены, а также любые содержащиеся в нем подблоки. Таким образом, 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 и т. Д. Для получения дополнительной информации о visit веб-сайт Babel

person Alireza    schedule 22.03.2017
comment
Не знаю, верен ли последний пример. Потому что, вызывая его не из функции, а из прямой командной строки, он по-прежнему считается частью той же функции. Итак, если вы вызвали его извне функции, он не должен вести себя таким же образом. - person ACopeLan; 28.08.2020

Есть некоторые тонкие различия, let область видимости ведет себя больше, чем область видимости переменных в более или менее любых других языках.

например Он относится к охватывающему блоку, они не существуют до объявления и т. Д.

Однако стоит отметить, что let является лишь частью новых реализаций Javascript и имеет разную степень поддержки браузером.

person olliej    schedule 17.04.2009
comment
Также стоит отметить, что ECMAScript является стандартом, а let включен в Черновик 6-го издания и, скорее всего, будет в окончательной спецификации. - person Richard Ayotte; 31.03.2012
comment
Вот и вся разница за 3 года: D - person olliej; 13.04.2012
comment
Просто ответил на этот вопрос, и в 2012 году только браузеры Mozilla поддерживают let. Safari, IE и Chome - нет. - person pseudosavant; 13.07.2012
comment
Идея случайного создания области видимости частичного блока по ошибке - хороший момент, будьте осторожны, let не поднимается, чтобы использовать переменную, определенную let, определенную в верхней части вашего блока. Если у вас есть оператор if, который представляет собой нечто большее, чем просто несколько строк кода, вы можете забыть, что вы не можете использовать эту переменную до тех пор, пока она не будет определена. БОЛЬШАЯ ТОЧКА !!! - person Eric Bishard; 07.05.2015
comment
Это одно из самых важных различий между let и var, и его нет в общепринятом ответе, ха-ха. Особенно учитывая многочисленные ошибки, которые могут возникнуть из-за подъема и определения объема. Я чувствую, что между let и var не так много различий, если вы не упоминаете подъем. - person Jay; 21.06.2015
comment
@EricB: да и нет: в ECMAScript 2015 let поднимет переменную в верхнюю часть блока. Однако ссылка на переменную в блоке перед объявлением переменной приводит к ошибке ReferenceError (мое примечание: вместо старого доброго undefined). Переменная находится во «временной мертвой зоне» от начала блока до обработки объявления. То же самое и с операторами switch, потому что существует только один базовый блок. Источник: developer.mozilla.org/en-US/ docs / Web / JavaScript / Reference / - person GitaarLAB; 21.05.2016

  • Переменная не поднимается

    let не будет поднимать всю область блока, в котором они появляются. В отличие от этого, var может подниматься, как показано ниже.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    Собственно, Пер @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 создает новую лексическую среду с этими именами для а) выражения инициализатора б) каждой итерации (прежде для оценки выражения приращения), более подробная информация о здесь.

person zangw    schedule 17.01.2016
comment
Ура, они поднимаются, но ведут себя так, как будто их не поднимают из-за (барабанной дроби) временной мертвой зоны - очень драматичное название идентификатора, недоступного до тех пор, пока он не объявлен :-) - person Drenai; 31.12.2016
comment
Так пусть поднят, но недоступен? Чем это отличается от «без подъема»? - person N-ate; 22.11.2017
comment
Надеюсь, Брайан или Берги вернутся, чтобы ответить на этот вопрос. Поднимается ли объявление let, но не переуступка? Спасибо! - person N-ate; 22.11.2017
comment
@ N-ate, вот одно сообщение Берги, может быть, вы найдете в нем ответ. - person zangw; 23.11.2017
comment
Интересно, что это даже называется подъемным, когда речь идет о сдаче внаем. Я понимаю, что технически механизм синтаксического анализа предварительно захватывает его, но для всех намерений и целей программист должен относиться к нему так, как будто его не существует. С другой стороны, подъем var имеет значение для программиста. - person N-ate; 24.11.2017

Разница заключается в области переменных, объявленных для каждой из них.

На практике разница в масштабах имеет ряд полезных последствий:

  1. let переменные видны только в их ближайшем охватывающем блоке ({ ... }).
  2. let переменные можно использовать только в строках кода, которые появляются после объявления переменной (даже если они подняты!).
  3. let переменные не могут быть повторно объявлены последующими var или let.
  4. Глобальные let переменные не добавляются к глобальному объекту window.
  5. let переменные просты в использовании с замыканиями (они не вызывают состояния гонки < / а>).

Ограничения, накладываемые let, уменьшают видимость переменных и увеличивают вероятность того, что неожиданные конфликты имен будут обнаружены раньше. Это упрощает отслеживание и анализ переменных, включая их достижимость (помогает освободить неиспользуемую память) .

Следовательно, let переменные с меньшей вероятностью будут вызывать проблемы при использовании в больших программах или когда независимо разработанные фреймворки комбинируются новыми и неожиданными способами.

var может быть полезен, если вы уверены, что хотите получить эффект однократной привязки при использовании замыкания в цикле (# 5) или для объявления видимых извне глобальных переменных в вашем коде (# 4). Использование var для экспорта может быть отменено, если _14 _ мигрирует из пространства транспилятора на базовый язык.

Примеры

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
person mormegil    schedule 22.05.2017
comment
№5 не вызван состоянием гонки. При использовании var вместо let, код эквивалентен: var i = 0; while (i < 5) { doSomethingLater(); i++; } i находится за пределами закрытия, и к моменту выполнения doSomethingLater() i уже был увеличен в 5 раз, следовательно, на выходе будет i is 5 пять раз. При использовании let переменная i находится внутри замыкания, поэтому каждый асинхронный вызов получает свою собственную копию i вместо использования «глобальной» копии, созданной с помощью var. - person Daniel T.; 02.06.2017
comment
@DanielT .: Я не думаю, что преобразование вывода определения переменной из инициализатора цикла что-то объясняет. Это просто обычное определение семантики for. Более точное преобразование, хотя и более сложное, представляет собой классическое преобразование for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i is $ {j} ), 125/*ms*/); })(i); }, которое вводит запись активации функции для сохранения каждого значения i с именем j внутри функции. - person mormegil; 25.07.2017

Вот пример, который можно добавить к тому, что уже написали другие. Предположим, вы хотите создать массив функций 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

Не позволяйте этому случиться с вами. Используйте линтер.

ПРИМЕЧАНИЕ. Это обучающий пример, предназначенный для демонстрации _22 _ / _ 23_ поведения в циклах и с закрытием функций, которые также будут легко понять. Это был бы ужасный способ складывать числа. Но общий метод сбора данных при закрытии анонимных функций может встретиться в реальном мире в других контекстах. YMMV.

person abroz    schedule 18.08.2014
comment
@aborz: Также очень крутой синтаксис анонимной функции во втором примере. Это то, к чему я привык в C #. Я кое-что узнал сегодня. - person Barton; 20.02.2015
comment
Исправление: Технически синтаксис функции стрелки описан здесь = ›разработчик .mozilla.org / en-US / docs / Web / JavaScript / Reference / - person Barton; 16.03.2015
comment
На самом деле вам не нужно let value = i;. Оператор for создает лексический блок. - person Toothbrush; 23.10.2015

Пусть следующие две функции покажут разницу:

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
}
person Abdennour TOUMI    schedule 17.12.2015

Объем блока 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, переменная 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

Global 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 (всегда внимательно относитесь к этому, если это действительно необходимо).

person Willem van der Veen    schedule 09.09.2018

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].

person Dmitry    schedule 08.07.2016
comment
это первый раз, когда я когда-либо видел, как кто-то ведет себя так, будто желательно переменное затенение. нет, цель let - не включать слежку - person John Haugeland; 24.11.2016
comment
цель? это конструкция, вы можете использовать ее как хотите, вот один из интересных способов. - person Dmitry; 24.11.2016

Также кажется, что, по крайней мере, в Visual Studio 2015, TypeScript 1.5, «var» допускает несколько объявлений одного и того же имени переменной в блоке, а «let» - нет.

Это не приведет к ошибке компиляции:

var x = 1;
var x = 2;

Это будет:

let x = 1;
let x = 2;
person RDoc    schedule 11.08.2015

ES6 представил два новых ключевых слова (let и const), заменяющие var.

Когда вам нужно замедление на уровне блока, вы можете использовать let и const вместо var.

В таблице ниже приведены различия между var, let и const.

введите описание изображения здесь

person Srikrushna    schedule 26.01.2020
comment
Поднятая колонна неправильная. Все они поднимают переменную. Разница с var в том, что они поднимаются, но не инициализируются значением undefined. Если бы они не поднимались, они не маскировали бы переменные с тем же именем во включающих блоках: stackoverflow.com/q/63337235/2326961 - person Maggyero; 11.08.2020

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

person Moslem Shahsavan    schedule 28.10.2017

При использовании 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 для каждой итерации цикла переменная ограничена и имеет общую копию.

person Ankur Soni    schedule 22.05.2018

Если я правильно прочитал спецификации, то let к счастью также можно использовать, чтобы избежать самовызывающиеся функции, используемые для имитации только закрытых членов - популярный шаблон проектирования, который снижает читаемость кода, усложняет отладку, не добавляет реальной защиты кода или другое преимущество - кроме, может быть, удовлетворения чьего-то желания семантики, так что прекратите ее использовать. / rant

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

См. "Эмуляция частных интерфейсов"

person Daniel Sokolowski    schedule 14.10.2016
comment
Не могли бы вы подробнее рассказать, почему выражения немедленного вызова функций не обеспечивают «защиту кода», а let это делает? (Я предполагаю, что вы имеете в виду IIFE с «функцией самозапуска».) - person Robert Siemer; 01.03.2020
comment
А зачем вы в конструкторе выставляете hiddenProperty? Есть только один hiddenProperty для всех экземпляров в вашем «классе». - person Robert Siemer; 01.03.2020

Некоторые хаки с 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)
person zloctb    schedule 21.07.2016
comment
пожалуйста, что это значит let { type, name, value } = node;? вы создаете новый объект с 3 свойствами типа / имя / значение и инициализируете их значениями свойств из узла? - person AlainIb; 15.06.2017
comment
В примере 3 вы повторно объявляете узел, который вызывает исключение. Все эти примеры также отлично работают с var. - person Rehan Haider; 09.01.2019
comment
Это не отвечает на вопрос; ему может быть полезно объяснение того, что делает каждый блок кода. - person TylerH; 13.04.2021

Ниже показано, как '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 также затронута. Это основное отличие.

person Piklu Dey    schedule 07.09.2019

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
}
person Vipul Jain    schedule 17.12.2017

пусть vs вар. Все дело в сфере.

переменные var являются глобальными и доступны практически везде, в то время как переменные let не являются глобальными и существуют только до тех пор, пока их не убьет закрывающая скобка.

Посмотрите мой пример ниже и обратите внимание, как переменная lion (let) по-разному действует в двух console.logs; он выходит за рамки 2-го 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.
person daCoda    schedule 18.04.2019

Как уже упоминалось выше:

Разница лишь в масштабах. Область var ограничена ближайшим функциональным блоком, а let областью действия ближайшего охватывающего блока, который может быть меньше функционального блока. Оба являются глобальными, если они находятся вне какого-либо блока. Давайте посмотрим на пример:

Пример1:

В обоих примерах у меня есть функция myfunc. myfunc содержит переменную myvar, равную 10. В моем первом примере я проверяю, равно ли myvar 10 (myvar==10). Если да, я снова объявляю переменную myvar (теперь у меня есть две переменные myvar) с помощью ключевого слова var и присваиваю ей новое значение (20). В следующей строке я печатаю его значение на своей консоли. После условного блока я снова печатаю значение myvar на своей консоли. Если вы посмотрите на вывод myfunc, myvar имеет значение, равное 20.

let keyword

Пример2: Во втором примере вместо использования ключевого слова var в условном блоке я объявляю myvar с помощью ключевого слова let. Теперь, когда я вызываю myfunc, я получаю два разных результата: myvar=20 и myvar=10.

Так что разница очень проста, то есть в ее объеме.

person N Randhawa    schedule 07.08.2018
comment
Пожалуйста, не публикуйте изображения кода, это считается плохой практикой в ​​SO, поскольку он не будет доступен для поиска будущими пользователями (а также из соображений доступности). Кроме того, этот ответ не добавляет ничего, чего еще не было в других ответах. - person inostia; 24.08.2018

Я хочу связать эти ключевые слова с контекстом выполнения, потому что контекст выполнения важен во всем этом. Контекст выполнения имеет две фазы: фазу создания и фазу выполнения. Кроме того, у каждого контекста исполнения есть переменная среда и внешняя среда (ее лексическая среда).

На этапе создания контекста выполнения var, let и const по-прежнему сохраняют свою переменную в памяти с неопределенным значением в среде переменных данного контекста выполнения. Разница в фазе исполнения. Если вы используете ссылку на переменную, определенную с помощью var, до того, как ей будет присвоено значение, она просто будет неопределенной. Никакого исключения не будет.

Однако вы не можете ссылаться на переменную, объявленную с помощью let или const, пока она не будет объявлена. Если вы попытаетесь использовать его до того, как он будет объявлен, то на этапе выполнения контекста выполнения возникнет исключение. Теперь переменная все еще будет в памяти благодаря этапу создания контекста выполнения, но движок не позволит вам ее использовать:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

С переменной, определенной с помощью var, если движок не может найти переменную в среде переменных текущего контекста выполнения, он поднимется по цепочке областей видимости (внешняя среда) и проверит переменную среду внешней среды для переменной. Если он не может найти его там, он продолжит поиск в Scope Chain. Это не относится к let и const.

Вторая особенность let - вводить область видимости блока. Блоки обозначены фигурными скобками. Примеры включают функциональные блоки, блоки if, блоки для блоков и т. Д. Когда вы объявляете переменную с помощью let внутри блока, переменная доступна только внутри блока. Фактически, каждый раз, когда блок запускается, например, в цикле for, он создает новую переменную в памяти.

ES6 также вводит ключевое слово const для объявления переменных. const также имеет блочную область видимости. Разница между let и const заключается в том, что переменные const необходимо объявлять с помощью инициализатора, иначе это приведет к ошибке.

И, наконец, когда дело доходит до контекста выполнения, переменные, определенные с помощью var, будут прикреплены к объекту this. В глобальном контексте выполнения это будет объект окна в браузерах. Это не относится к let или const.

person Donato    schedule 13.02.2019

Поскольку в настоящее время я пытаюсь глубоко разобраться в JavaScript, я поделюсь своим кратким исследованием, которое содержит некоторые из уже обсужденных замечательных частей, а также некоторые другие детали в другой перспективе.

Понять разницу между var и let станет проще, если мы поймем разницу между function и областью действия.

Рассмотрим следующие случаи:

(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 :(
}
person Lucian Nut    schedule 11.03.2019

Я только что наткнулся на один вариант использования, в котором мне пришлось использовать var вместо let, чтобы ввести новую переменную. Вот такой случай:

Я хочу создать новую переменную с динамическими именами переменных.

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

Приведенный выше код не работает, потому что eval вводит новый блок кода. Объявление с использованием var объявит переменную вне этого блока кода, поскольку var объявляет переменную в области видимости функции.

let, с другой стороны, объявляет переменную в области видимости блока. Таким образом, переменная a будет видна только в блоке eval.

person Sarvar Nishonboyev    schedule 25.10.2020
comment
Когда вам когда-нибудь нужно будет создать имя динамической переменной и получить к нему доступ позже? Намного лучше создать объект и присвоить ему ключи и значения. - person typeof null is object; 09.11.2020

Теперь я думаю, что есть лучшая область видимости переменных для блока операторов с использованием 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 после этого, чтобы у них была такая же область видимости в JavaScript, как и в других языках, Java, C # и т. Д.

Люди, не имеющие четкого представления об области видимости в JavaScript, раньше допускали ошибку.

Подъем не поддерживается с помощью let.

При таком подходе ошибки, присутствующие в JavaScript, удаляются.

Обратитесь к Подробно о ES6: let и const, чтобы лучше понять это.

person swaraj patil    schedule 01.07.2016
comment
Для более глубокого понимания обратитесь к ссылке - davidwalsh.name/for-and-against-let - person swaraj patil; 01.07.2016

В этой статье четко определяется разница между var, let и const.

const - это сигнал о том, что идентификатор не будет переназначен.

let, это сигнал о том, что переменная может быть переназначена, например, счетчик в цикле или замена значения в алгоритме. Он также сигнализирует о том, что переменная будет использоваться только в блоке, в котором она определена, а это не всегда вся содержащая функция.

var теперь самый слабый сигнал, доступный при определении переменной в JavaScript. Переменная может быть или не быть переназначена, и переменная может или не может использоваться для всей функции или только для цели блока или цикла.

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

person anandharshan    schedule 27.12.2016

Я думаю, что термины и большинство примеров немного утомительны. Основная проблема, с которой я лично столкнулся с этой разницей, - это понимание того, что такое «Блок». В какой-то момент я понял, что блоком будут любые фигурные скобки, кроме оператора 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);

person Rafael Herscovici    schedule 28.04.2019

Эта диаграмма - лучший способ понять разницу между let, const и var. введите здесь описание изображения Глобальная область: Область, в которой любая функция может вызывать эту переменную.

Область действия функции: переменную можно использовать в теле функции.

Область действия блока: переменная, которую можно использовать между двумя фигурными скобками.

person Shoaib Muhammad Arif    schedule 15.07.2021
comment
Где вы нашли эту диаграмму? - person TylerH; 15.07.2021
comment
@TylerH из моих заметок профессора, есть ли проблема? - person Shoaib Muhammad Arif; 15.07.2021
comment
Да, это хорошо известное изображение из серии руководств по JavaScript от 2017 года; похоже, что ваш профессор взял его и использовал без указания авторства. В материалах из других мест должна быть указана надлежащая атрибуция, независимо от того, размещены ли они здесь или используются в конспектах лекций профессора. Кроме того, информация на графике уже присутствует в большинстве / всех предыдущих ~ 50 ответах. - person TylerH; 15.07.2021
comment
@TylerH благодарит вас за эту информацию - person Shoaib Muhammad Arif; 15.07.2021

Проверьте эту ссылку в MDN

let x = 1;

if (x === 1) {
let x = 2;

console.log(x);
// expected output: 2
}

console.log(x);
// expected output: 1
person Nurlan    schedule 18.05.2018
comment
Кроме того, было бы полезно объяснить словами, что делает let, хотя это сделало бы его дубликатом другого ответа здесь - person pushkin; 31.05.2018
comment
Он пытается сказать, что let вне if определяет переменную x=1. Вызов if-statement (начиная с x===1 is true. Теперь наступает сложная часть, которая также является основной причиной, по которой вам нужно отслеживать let vs var. Внутри if он пытается установить x=2, который в console.log(x) напечатает 2, ОДНАКО, x снаружи if по-прежнему имеет значение 1, поэтому другой console.log(x) дает 1, поскольку значение x в глобальном масштабе по-прежнему 1. Ответ не объясняет этого, поэтому не следует считать хорошим ответом в моем мнение. - person Thomas Darvik; 27.09.2018

введите здесь описание изображения

Взгляните на это изображение, я создал один очень простой пример для демонстрации переменных const и let. Как видите, при попытке изменить переменную const вы получите сообщение об ошибке (Попытка переопределить «имя», которое является постоянным »), но посмотрите на переменную let ...

Сначала мы объявляем let age = 33, а затем назначаем другое значение age = 34;, и это нормально, у нас нет ошибок, когда мы пытаемся изменить let переменную.

person Mile Mijatović    schedule 16.02.2019
comment
Код следует записывать в сообщениях SO, а не делиться ими через снимок экрана. - person TylerH; 13.04.2021

Область действия переменной var - это функция и глобальная область, но область действия переменной let - область блока (фигурная скобка {}).

const myFun=()=> {
      var str1 = "hello";
      let str2 = "program";
    
      console.log(str1, str2); // hello program
    
      {
        var myvar1 = "hiii"
        let myvar2 = "ooo";
        console.log(myvar1, myvar2); // hiii ooo
      }
    
      console.log(myvar1); // hiii
      console.log(myvar2); // ReferenceError
    }
    console.log(myvar1); // not defined
    myFun();
person Force Bolt    schedule 04.05.2021
comment
Это уже покрыто принятым ответом (и большинством других тоже). При новых ответах, особенно на старые вопросы, следует позаботиться о том, чтобы не повторять существующий контент. Скорее они должны сосредоточиться на новых решениях проблемы или ответах на вопрос. - person TylerH; 04.05.2021

ECMAScript 6 добавил еще одно ключевое слово для объявления переменных, кроме "const", кроме "позволять".

Основная цель введения «let» и «const» вместо «var» - иметь область видимости блока вместо традиционной лексической области видимости. В этой статье очень кратко различие между var и let, а также обсуждение const.

person Gurucharan M K    schedule 19.05.2016

До 2015 года использование ключевого слова var было единственным способом объявить переменную JavaScript.

После ES6 (версия JavaScript) он допускает 2 новых ключевых слова: let и const.

let = можно переназначить
const = нельзя переназначить (const, которое происходит от константы, краткой формы 'const')

Пример:

  • Предположим, что для объявления названия страны / имени вашей матери здесь больше всего подходит const. потому что у вас меньше шансов изменить название страны или фамилию матери рано или поздно.

  • Ваш возраст, вес, зарплата, скорость велосипеда и другие подобные данные, которые часто меняются или требуют переназначения. в этих ситуациях используется let.

person Taib Islam Dipu    schedule 15.12.2020

Используйте ключевое слово var только тогда, когда вы хотите установить глобальную переменную в своем скрипте или вы хотите повторно объявить ту же переменную в той же области. Когда появился ES2015, используйте ключевое слово let, если вы хотите установить переменную в качестве области действия функции, области блока, области цикла или не хотите повторно объявлять переменную в той же области.

person Pranay Singh    schedule 07.05.2021
comment
Привет, Праная, пожалуйста, не повторяйте существующие ответы. Уже есть множество ответов, которые исчерпывающе охватывают этот вопрос. - person TylerH; 07.05.2021