Quoting from the book JavaScript: The Definitive Guide
Unlike variables declared with
let
, it is legal to declare the same variable multiple times withvar
. And becausevar
variables have function scope instead of block scope, it is actually common to do this kind of redeclaration. The variablei
is frequently used for integer values, and especially as the index variable of for loops. In a function with multiple for loops, it is typical for each one to beginfor(var i = 0;
.... Becausevar
does not scope these variables to the loop body, each of these loops is (harmlessly) re-declaring and re-initializing the same variable.
I am unable to understand what is meant in this paragraph. First I assumed the following would not have worked with second loop not iterating, because i
would be function scoped:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
}
for (let i = 0; i < 2; i++) {
console.log(i);
}
})();
but it prints
0
1
0
1
Then I assumed this would have printed 0 0 1 1 0 1
, which is also not the case:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Can someone help me understand what is meant by
in a function of multiple for loops,
var
can be used harmlessly in each loop
and how it is different to let
?
To me it looks like the opposite it true (which is also very confusing) where let
can be used harmlessly in functions with multiple loops:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
for (let i = 0; i < 2; i++) {
console.log(i);
}
}
})();
prints:
0
0
1
1
0
1
let
variables are forbidden to be re-declared in the same scope, like the following:
let foo = 10;
let foo = 20;
var
variables can be, though:
var foo = 10;
var foo = 20;
console.log('ok');
In your first snippet, variables declared with let
in the header of a for
loop only exist inside the loop; they're block scoped, and aren't scoped to the outer block or function:
(function foo() {
for (let i = 0; i < 2; i++) {
// console.log(i);
}
for (let i = 0; i < 2; i++) {
// console.log(i);
}
// doesn't exist out here:
console.log(i);
})();
The let i
s above create i
s in two different scopes, which is why it's not forbidden. Similarly:
(function foo() {
{
let i = 10;
}
{
let i = 20;
}
console.log('OK up until here');
// doesn't exist out here:
console.log(i);
})();
var
s, in contrast, have function scope, not block scope - with your var
version of the loop, since there's only one function, this:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
is equivalent to
(function foo() {
var i; // the `i` variable exists at this level
for (i = 0; i < 2; i++) {
console.log(i);
for (i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Then I assumed this would have printed 0 0 1 1 0 1, which is also not the case:
i
gets initialized to zero at the beginning of each loop.
The first loop begins once, at the start of the function.
The second loop begins just after logging i
, on each iteration of the outer loop.
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
So you get 0 0 1
logged.
What the article probably means by
in a function of multiple for loops, var can be used harmlessly in each loop
is that it can work if both loops are on the same level of the function, eg
for (...) {
}
for (...) {
}
It definitely won't work with nested loops, if both loops use the same variable, because there's only ever a single i
variable, rather than one for each loop.
where let can be used harmlessly in functions with multiple loops:
For nested loops, yes, since variables declared with let
are unique to each loop, rather than being shared across the whole containing function.