Замыкания внутри циклов в JavaScript. Какова механика работы?
в курсе сюрпризов JavaScript относительно замыкание внутри closures.
Если для вас это все еще сюрпризы - рекомендую перештудировать какой-нибудь туториал или документацию. В JS все как-будто асинхронно, на самом деле там просто есть event loop.
Давайте разберемся на примере:for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}
Что у нас тут будет происходить. Мы берем цикл и 10 раз создаем отложенный вызов. Что при этом происходит... давайте представим себе что в JS все разбито на кадры. Цикл - один кадр, и пока он не отработает, какой бы он длинный не был - новый кадр так же не отработает. setTimeout не просто выполняет код с задержкой, через 1000 милисекунд в event loop будет добавлен очередной кадр. И любой "асинхронный" вызов так же просто добавляет кадр в event loop. Внутри же event loop все выполняется синхронно и по порядку.
Так вот, на момент, когда закончит выполнение кадр с циклом, значение i уже будет установлено в 9. А как мы выяснили ранее,  JS ничего более не будет выполнять до этого момента. Посему отложенный код выведет нам одно и то же значение так как все они ссылаются на одну и ту же переменную.
Теперь, что происходит тут:for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);
        }, 1000);
    })(i);
}
Да собственно то же самое. Только за счет того что мы при публикации setTimeout используем замыкание и передаем i в качестве аргумента, срабатывает механизм называемый copy-on-write. То есть в замыкании не i а его копия (когда цикл перезаписывает значение i - все кто ссылался на это значение не по ссылке, копируют себе переменную, для простоты можно просто думать что каждый раз когда вы передаете что-то не по ссылке, происходит копирование).
Так как у каждого кадра, выполняемого в setTimeout есть своя копия i с нужным значением - все будет хорошо.
Нужно использовать let вместо varfor(let i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}