Я видел много недоразумений между var и let в JavaScript.

На первый взгляд различия кажутся обманчиво простыми:

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

Однако есть некоторые проблемы, связанные с производительностью, о которых мало кто знает, особенно связанные с ужасно звучащей «временной мертвой зоной». По этой причине я рекомендую использовать let только в том случае, если у вас есть особая потребность в let - подробнее об этом позже.

Что такое временная мертвая зона?

Я видел некоторую дезинформацию об этом по всему Интернету. Многие ответы stackoverflow по этой теме либо неверны, либо, по крайней мере, немного вводят в заблуждение. Обязательно обратитесь к самым надежным источникам по ES 6, где вы можете найти необычные функции языка JavaScript.

Вот объяснение временной мертвой зоны MDN:

В ECMAScript 2015 привязки let не подвергаются подъему переменных, что означает, что объявления let не перемещаются в начало текущего контекста выполнения. Ссылка на переменную в блоке перед инициализацией приводит к ошибке ReferenceError (в отличие от переменной, объявленной с помощью var, которая будет иметь только неопределенное значение). Переменная находится в «временной мертвой зоне» с начала блока до обработки инициализации.

Само по себе это не кажется проблемой. Многие разработчики предпочли бы получить сообщение об ошибке при попытке использовать еще не инициализированную переменную, а не выполнять код с переменной, имеющей значение undefined (что происходит при подъеме переменной). Я согласен с этим; Я бы предпочел получить сообщение об ошибке, чем молча отказываться от моего кода.

Но временная мертвая зона вам не друг…

Однако из-за этой временной мертвой зоны возникают некоторые проблемы с производительностью. На самом деле в репозитории GitHub для Node.js есть открытый вопрос.

Вы можете поэкспериментировать с этим фрагментом кода, чтобы воочию увидеть эти проблемы с производительностью:

console.time (‘var’);
for (var i = 0; i ‹100000000; i ++) {}
console.timeEnd (‘ var ’); // около 50 мс на моей машине

console.time ('let');
for (let j = 0; j ‹100000000; j ++) {}
console.timeEnd ('let'); // около 200 мс на моей машине

В этом есть много причуд, которые лучше всего понять, прочитав проблему на странице github, но основные моменты:

  1. let обычно работает медленнее, чем var
  2. некоторая более медленная производительность вполне ожидаема и связана с основной функциональностью let (больше контекстов выполнения! иногда вам это нужно!)
  3. наличие переменных во временной мертвой зоне может резко замедлить, казалось бы, несвязанный код в той же области. Если вам нужны доказательства, попробуйте приведенный ниже пример:

console.time (‘var’);
for (var i = 0; i ‹100000000; i ++) {}
console.timeEnd (‘ var ’); // теперь намного медленнее, около 300 мс

console.time ('let');
let j // это объявление помещает j в ту же область видимости, что и i, увеличивая // проблемы производительности TDZ
for (j = 0; j ‹100000000; j ++ ) {}
console.timeEnd ('пусть'); // все еще 300+ мс

Мои рекомендации для решения этой проблемы:

  1. Ограничьте использование let тем, что вам действительно нужно, чтобы переменная была заблокирована
  2. Поднимите вручную - переместите объявление let в верхнюю часть области действия, чтобы избежать временной мертвой зоны.
  3. Добавьте дополнительные скобки, чтобы ограничить объем кода в той же области, что и объявление let, на который может случайно повлиять эта проблема с производительностью.

console.time (‘var’);
for (var i = 0; i ‹100000000; i ++) {}
console.timeEnd (‘ var ’); // производительность var снова повышается! : D

console.time ('let');
{// эти дополнительные скобки предотвращают распространение проблемы производительности TDZ
let j
for (j = 0; j ‹100000000; j ++) {}
}
console.timeEnd ('пусть');

Когда мне действительно нужно использовать let?

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

Этот пример из MDN очень хорошо освещает эту проблему ...

var messages = [Meow !, Я говорящий кот! Обратные вызовы - это весело!];

for (var i = 0; i ‹messages.length; i ++) {
setTimeout (function () {
console.log (messages [i]);
}, i * 1500);
}

Этот код будет console.log undefined три раза, потому что i был увеличен до 3 перед фактическим выполнением любой из функций обратного вызова из setTimeouts. Объявление i как let решит для нас эту проблему.

Напишите мне на [email protected], если вы можете предложить какие-либо другие решения проблем, возникающих из-за временной мертвой зоны, или других ситуаций, в которых нам абсолютно необходимо разрешить!