Я видел много недоразумений между 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, но основные моменты:
- let обычно работает медленнее, чем var
- некоторая более медленная производительность вполне ожидаема и связана с основной функциональностью let (больше контекстов выполнения! иногда вам это нужно!)
- наличие переменных во временной мертвой зоне может резко замедлить, казалось бы, несвязанный код в той же области. Если вам нужны доказательства, попробуйте приведенный ниже пример:
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+ мс
Мои рекомендации для решения этой проблемы:
- Ограничьте использование let тем, что вам действительно нужно, чтобы переменная была заблокирована
- Поднимите вручную - переместите объявление let в верхнюю часть области действия, чтобы избежать временной мертвой зоны.
- Добавьте дополнительные скобки, чтобы ограничить объем кода в той же области, что и объявление 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], если вы можете предложить какие-либо другие решения проблем, возникающих из-за временной мертвой зоны, или других ситуаций, в которых нам абсолютно необходимо разрешить!