I cannot understand why this code is behaving as it is:
for (var i = 1; i < 3; i++) {
var j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's " + j);
}
}
(jsFiddle)
If I set j
to null
and check for null
, it works the way I think it should.
This isn't how Java, C#, C++, etc. work, hence, the confusion.
This is because variables in JavaScript are scoped to functions (or the global scope if not within a function). A var
is not scoped inside of a loop, and curly braces do not defined a new closure. Basically, the value of j
persists after the loop body, and the var
does not re-define j
as undefined
. This is why explicitly setting var j = null;
has the expected effect.
A good way to think about this is, any time you declare a variable with var
, like this.
function someFunc() {
for(var i = 0; i < 3; i++){
var j;
}
}
The interpreter hoist the variable declarations like so.
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
}
}
Notice that since the var j
declaration is hoisted to the top of the function, the declaration actually does nothing within the loop.
However, if you were to initialize the variable with null
like this.
function someFunc() {
for(var i = 0; i < 3; i++){
var j = null;
}
}
It would be interpreted like this.
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
j = null;
}
}
Notice how for every loop, j
is set to null
.
There is a keyword in ES6 which will create a scope in a loop like this, it is the let
keyword. Keep in mind that browser support for the let
keyword is poor at this point.
for (var i = 1; i < 3; i++) {
let j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's "+ j);
}
}
The first time your for
loop executes the body of the loop, j
is undefined and thus your code sets j=1
. On the subsequent iterations of the loop, j
is already defined and set to 1
so it goes into your else
clause as would be expected.
This occurs because variables defined with var
in Javascript are function scoped, not block scoped and if not inside a function, then they are global. So, there is only one variable j
in your jsFiddle and each iteration of the for
loop uses the same variable (thus inheriting the value from the previous iteration).
It will work if you initialize j = null;
inside the body of the for
loop because then you're reinitializing it for each iteration rather than using the value from the previous iteration.
ES6 proposes to add the let
declaration which would scope to the nearest block. See What's the difference between using "let" and "var" to declare a variable? for more info on let
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With