Javascript 'let' и 'var' для for-loops

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

Я не ожидал большой разницы, и Firefox соответствовал моим ожиданиям:

jsPerf результаты на Firefox 52

Но в Chromium произошло что-то странное:

Результаты jsPerf в Chrome 57

Мало того, что все результаты теста значительно ниже, но let внутри цикла прерывается до некоторой части скорости.

Я решил запустить тесты в браузере, чтобы убедиться, что это не моя причудливая настройка Linux. То же самое происходит с Firefox 53 и Chrome 58 в Windows 10. Я даже тестировал несколько более старое Chrome 50 и получил то же поведение.

Что происходит? Это ошибка?

EDIT: Некоторые отметили, что цикл, вероятно, просто оптимизирован, поскольку он ничего не делает. Чтобы показать, что это не так, я изменил тест.

Ответ 1

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

let sum = 0;
let fns = [];
for (let i=0; i < 1000; i++) {
  function foo() { sum += i; }

  fns.push(foo);

}

Когда вы запускаете его через babeljs, вы можете видеть, что эквивалентный код ES5, который он производит, включает вызов функции, чтобы сохранить правильные сроки жизни переменных:

var sum = 0;
var fns = [];

var _loop = function _loop(i) {
  function foo() {
    sum += i;
  }

  fns.push(foo);
};

for (var i = 0; i < 1000; i++) {
  _loop(i);
}

Однако babel достаточно умен, что, если вы не делаете ничего, что требует продления жизни переменной цикла, он просто использует обычный цикл for с встроенным телом. Итак, ваш код:

for (let i=0; i < 1000; i++) {
  true;
}

может быть показано, что он в точности эквивалентен:

for (var i=0; i < 1000; i++) {
  true;
}

Я предполагаю, что что-то очень похожее происходит внутри Chrome, но они еще не оптимизировали случаи, когда им не нужно сохранять переменную цикла.

Было бы интересно посмотреть, как код, который я использовал в начале этого примера, сравнивается в Firefox и Chrome, поскольку я подозреваю, что оба они тоже будут медленными. Вы должны остерегаться синхронизации вещей, таких как пустые циклы, поскольку результаты могут быть искажены оптимизацией намного больше, чем обычно для реального кода.

Ответ 2

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