There is a difference of the scope with let and var, which causes them to behave differently.
Here is a quote from other Stack Overflow answer about the differences between var and let.
The main difference is scoping rules. Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
So, in summary, var is referring to the same variable address. But, as let is blocked scope (according to the quote above), every callback in setTimeout() will make i have a different value then the previous one.
An experiment that is possible is to make let behave like var. To make let behave like var, you can use (and run) the code below.
To see how let is behaving like var, read ahead about var hoisting!
let i;
for (i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
As we moved let to the main scope, it behaves like var (as var is positioned at the top of the file with a value of undefined at runtime).
This makes the let variable behave like var, making the output of the for loop the same as if the let variable was var.
Thanks to Nick Vu for the experiment!
Here is a quick overview of var hoisting below.
The actual code (shown below):
web = "stackoverflow.com";
var web;
Is understood as:
var web;
web = "stackoverflow.com";
Basically, defining a variable with var anywhere will always result it being at the top of the file, resulting in the weird behaviour with the for loop.
This is why many people prefer let over var; because of how confusing var hoisting can be!
Always use let, unless var is absolutely needed!