Что будет выведено в консоли при объявлении массива функций с использованием var в цикле?
Что проверяют
Понимаете ли вы, что var не имеет блочной области видимости, и что замыкание хранит ссылку на переменную, а не копию её значения на момент создания функции. Вопрос простой, но по нему сразу видно, читали ли вы про области видимости что-то дальше синтаксиса.
Как отвечать
Сразу назовите результат и объясните механизм.
var fns = [];
for (var i = 0; i < 3; i++) {
fns.push(function () { console.log(i); });
}
fns.forEach(function (f) { f(); }); // 3, 3, 3var i поднимается в область видимости функции или в глобальную, поэтому переменная одна на весь цикл. Все три функции замкнулись на одну и ту же ячейку. К моменту вызова цикл уже закончился, i равна 3, и это значение видят все колбэки.
С let вывод будет 0, 1, 2: на каждой итерации создаётся новая привязка, в которую копируется значение с предыдущей. Если нужно то же самое на var, помогает дополнительная функция, принимающая значение параметром:
for (var i = 0; i < 3; i++) {
(function (n) { fns.push(function () { console.log(n); }); })(i);
}Тот же ответ годится для setTimeout внутри цикла: там разница нагляднее, потому что колбэки заведомо выполняются после цикла.
Где спотыкаются
Говорят «выведет 0, 1, 2», потому что мысленно подставляют значение в момент создания функции. Замыкание захватывает переменную целиком, и её значение читается в момент вызова, а не объявления.
Второе: забывают про последнее значение счётчика. Для цикла до 3 называют 2, хотя цикл выходит на 3: сначала инкремент, потом проверка условия, и только потом выход.
Третье: объясняют разницу let и var фразой «let блочная» и останавливаются. Интервьюер обычно спрашивает дальше, что будет при обращении к переменной до объявления, и тут всплывает временная мёртвая зона.