the scoping effect of the initialization block can be understood as if the declaration happens within the loop body, but just happens to be accessible within the condition and update parts.
const { log } = console;// โญโโ i โโโฎ <--- โญ `i` is "block-scoped"for (let i =0; i <3; i++) {// โญ every time "block scope" is entered,// a new/different `i` is captured by closure.//// โญโ closure โโโฎsetTimeout( () =>log(i) ,1000); // 0, 1, 2 โญ }let j =0; // โ `j` is in "outer scope"for ( ; j <3; j++) {// โ every closure is capturing the same `j` variable//// โญโ closure โโโฎsetTimeout( () =>log(j) ,2000); // 3, 3, 3 โ (j = 3 after for-loop)}var keep1 = [];// โญโโ i โโโฎ <--- โ "var" has no block scopefor (var i =0; i <3; i++) {// โ every closure is keeping the "same" `i`. keep1[i] =functionkeepI(){return i; };}var keep2 = [];// โญโโ i โโโฎ <--- โญ "let" is block-scopedfor (let i =0; i <3; i++) {// โญ `i` is block-scoped, every time block scope is entered,// a new/different `i` is created & kept by closure. keep2[i] =functionkeepI(){return i; };}[keep1.map(f =>f()),// โ [ 3, 3, 3 ]keep2.map(f =>f()),// โญ [ 0, 1, 2 ]].forEach(x =>log(x));