Comments 51
Третий вариант:
const arr = [10, 12, 15, 21];
arr.forEach(function (item, i) {
setTimeout(function () {
console.log('Index: ' + i + ', element: ' + item);
});
});
PS если уж и оставляли ссылку на SO — можно было бы и на русскоязычное объяснение сослаться. Например, на вот это: https://ru.stackoverflow.com/a/433888/178779
setTimeout(function(item, i) {
console.log('Index: ' + i + ', element: ' + item);
}.bind(this, arr[i], i), 3000);
Вы цикл забыли, вот у вас и вышло "покороче". И, раз уж вы решили так делать — проще пойти через дополнительные параметры setTimeout:
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
setTimeout(function(i) {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000, i);
}
[[scope]]
i = 4).mayorovp, можете пояснить что происходит здесь, для тех кто не пишет на JavaScript (не знаком с асинхронными функциями)?
Из вашего примера: в функцию setTimeout добавили параметр i, который сохраняет изменённое значение для передачи анонимной функции (хоть setTimeout выполняется с задержкой по таймеру).
Более современный вариант:
const arr = [10, 12, 15, 21];
for (const [i, item] of arr.entries()) {
setTimeout(function () {
console.log(`Index: ${i}, element: ${item}`);
});
}
var timerId = setTimeout(func / code, delay[, arg1, arg2...])
Правда не сработает на <IE9
const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
})(i)
}
А вот ваше уточнение про таймаут 0 и однопоточность не очень понятно, что мы получим?
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
}
Вообще это не хитрый а самый что ни есть базовый вопрос по js на любую позицию кроме может совсем зелёного джуна. Если человек утверждает что знает JS и работает на нём профессионально, то незнание этих вещей означает полную профнепригодность в принципе. Скоупы, что их создаёт и как работает замыкание являются базовыми знаниями для языка который на этом построен чуть менее чем полностью.
К тому же кандидат мог почитать статьи о часто задаваемых вопросах на собеседованиях и тупо выучить как правильно ответить, все же не мешало бы просто отдельно спросить стандартные вопросы:
«Какие типы функций вы знаете и какие особенности у каждого?
Что такое замыкания и область видимости переменной?
Что такое setTimeout/setInterval, чем отличаются?»
… и тд.
Хмм. Следует ли из вышесказанного что в циклах, в которых мы не хотим создавать такие замыкания, var i
будет работать чуть быстрее, чем let i
, ведь интерпретатору не надо создавать новую переменную i
на каждой итерации?
Производительность JS это, конечно, мутно, но я не настоящий сварщик.
Возьмем код
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log('The index of this number is: ' + i);
}, 3000);
}
for (let i = 0; i < arr.length; i++) {
console.log(i);
}
Вставим сюда и будет результат:
'use strict';
var arr = [10, 12, 15, 21];
var _loop = function _loop(i) {
setTimeout(function () {
console.log('The index of this number is: ' + i);
}, 3000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
for (var i = 0; i < arr.length; i++) {
console.log(i);
}
Видно, что когда лишние телодвижения не нужны, babel транслирует в код 1-в-1. В случае если нужно делать замыкание на внутреннюю переменную (как в задаче в примере) — ее создание неизбежно, поэтому как не крутите, память будет выделена. Думаю, движок хрома делает еще более оптимальный байткод, так что на производительность влиять не будет.
console.time('loop');
for (let i = 0; i < 1000000000; i++) {}
console.timeEnd('loop');
// loop: 685.50390625ms
console.time('loop');
for (var i = 0; i < 1000000000; i++) {}
console.timeEnd('loop');
// loop: 2530.260986328125ms
Хитрый вопрос? Хитрее только "чему равен typeof null".
Вот вам еще хитрый вопрос для написания статьи на знание основ js:
function foo() {
'use strict';
console.log(bar());
function bar() { return 'bar'};
}
Будет undefined, reference error или 'bar'?
Как вариант решения задачки из топика — даешь больше es6:
const arr = [10, 12, 15, 21];
arr.forEach((item, i) => setTimeout(function() {
console.log('Index: ' + i + ', element: ' + item);
}, 3000));
А вообще
Rx.Observable.from([10, 12, 15, 21]).delay(3000).do(console.log);
Вот такой код нормальный результат выдаёт:
const arr = [10, 12, 15, 21];
const iter = (i) => {
if (i >= arr.length) {return ;}
setTimeout(function() {
console.log('Index: ' + i + ', element: ' + arr[i]);
}, 3000);
return iter(i + 1);
};
iter(0);
Во-первых, почему последнее значение i — 4, а не 3?
Во-вторых, де-факто в консоли выводится иное…
В консоли выводится то же самое, просто некоторые консоли умеют отслеживать дублирующиеся сообщения и оставлять только одно.
А 4 выводится потому что после окончания цикла переменная i принимает именно это значение. На значении 3 цикл закончиться не может, потому что 3 < arr.length. Цикл заканчивается когда нарушается его условие — а оно нарушается когда i >= arr.length.
У меня на собеседовании по Питону похожее спрашивали. В цикле создается список лямбд, каждая из которых ссылается на счетчик. Функции выполнятся уже когда счетчик будет максимальным, из-за лексической видимости будет напечатано последнее значение.
Вот такой код:
local cb = {}
local arr = {1,2,3,4}
local counter = 1
for i = 1, 4 do
table.insert(cb, function ()
print('index: ' .. i .. ', element: ' .. arr[i] .. ', counter:' .. counter)
end)
counter = counter + 1
end
for i = 1, #cb do
cb[i]()
end
Даст вот такой результат:
index: 1, element: 1, counter:5
index: 2, element: 2, counter:5
index: 3, element: 3, counter:5
index: 4, element: 4, counter:5
Хитрый вопрос по JavaScript, который задают на собеседованиях в Google и Amazon