Why let and var bindings behave differently using setTimeout function?
With var
you have a function scope, and only one shared binding for all of your loop iterations - i.e. the i
in every setTimeout callback means the same variable that finally is equal to 6 after the loop iteration ends.
With let
you have a block scope and when used in the for
loop you get a new binding for each iteration - i.e. the i
in every setTimeout callback means a different variable, each of which has a different value: the first one is 0, the next one is 1 etc.
So this:
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}
})();
is equivalent to this using only var:(function timer() {
for (var j = 0; j <= 5; j++) {
(function () {
var i = j;
setTimeout(function clog() { console.log(i); }, i * 1000);
}());
}
})();
using immediately invoked function expression to use function scope in a similar way as the block scope works in the example with let
.It could be written shorter without using the j
name, but perhaps it would not be as clear:
(function timer() {
for (var i = 0; i <= 5; i++) {
(function (i) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}(i));
}
})();
And even shorter with arrow functions:(() => {
for (var i = 0; i <= 5; i++) {
(i => setTimeout(() => console.log(i), i * 1000))(i);
}
})();
(But if you can use arrow functions, there's no reason to use var
.)This is how Babel.js translates your example with let
to run in environments where let
is not available:
"use strict";
(function timer() {
var _loop = function (i) {
setTimeout(function clog() {
console.log(i);
}, i * 1000);
};
for (var i = 0; i <= 5; i++) {
_loop(i);
}
})();
Thanks to Michael Geary for posting the link to Babel.js in the comments. See the link in the comment for a live demo where you can change anything in the code and watch the translation taking place immediately. It's interesting to see how other ES6 features get translated as well. let vs var in javascript
First of all, the output will be four times and not five times(as mentioned in your comment).
I pasted your code in Babel REPL and this is what I got,
"use strict";
var arr = [1, 2, 3, 4];
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
Do you see how let works internally now? :-) let' in for loop with setTimeout()
If you can't use let
, you can always use an Immediately Invoked Function Expression:
let timer = () => { for (var i = 0; i < 5; i++) { (i => { setTimeout(() => { console.log(i) }, i * 1000) })(i) }}timer();
What is the difference between let and var?
Scoping rules
The main difference is scoping rules. Variables declared byvar
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).function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
}
run();
What is the difference between let and var?
Scoping rules
The main difference is scoping rules. Variables declared byvar
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).function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
}
run();
Explanation of `let` and block scoping with for loops
No, it's more than syntactic sugar. The gory details are buried in §13.6.3.9Is this just syntactic sugar for ES6?
CreatePerIterationEnvironment
.If you use thatHow is this working?
let
keyword in the for
statement, it will check what names it does bind and then- create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression)
- copy the values from all variables with those names from one to the next environment
for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i));
desugars to a simple// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
i++;
…
while for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i));
does "desugar" to the much more complicated// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
i = 0;
__status = {i};
}
{ let {i} = __status;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
if (i < 10)
process.nextTick(_ => console.log(i))
__status = {i};
} { let {i} = __status;
i++;
…
Related Topics
Angularjs Does Not Send Hidden Field Value
Rotate Object on Specific Axis Anywhere in Three.Js - Including Outside of Mesh
How to Know When All Promises Are Resolved in a Dynamic "Iterable" Parameter
JavaScript Summing Large Integers
How to Export Socket.Io into Other Modules in Nodejs
How to Remove Text (Without Removing Inner Elements) from a Parent Element Using Jquery
Jsf/Primefaces Ajax Updates Breaks Jquery Event Listener Function Bindings
Uncaught Typeerror: Cannot Use 'In' Operator to Search for 'Length' In
Handling Key-Press Events (F1-F12) Using JavaScript and Jquery, Cross-Browser
How to Sort an Associative Array by Its Values in JavaScript
Regular Expression to Match A, Ab, Abc, But Not Ac. ("Starts With")
React-Router Getting This.Props.Location in Child Components
JavaScript Closure Not Working