Reputation: 34084
This is some specific subtlety to do with Javascript scoping and global vs local variables that I don't quite understand - and can result in the surprising situation of the code within an if
condition influencing whether that if
condition is evaluated: code being influenced by code that hasn't 'happened' yet.
I've whittled it down to this JSBIN DEMO comparing two near-identical chunks of code.
In the first, everything happens as expected. We define a global variable, then within a scope, we check if it's defined, see that it is, and carry on:
someVar = function(){}; // or, window.someVar =...
(function(){
console.log("before", someVar, typeof someVar);
if(typeof someVar == 'undefined'){
someVar = true;
}
console.log("after", someVar, typeof someVar);
})();
The console shows the variable is a function and the if is skipped, as expected.
However, if we change the line within the if
to use var
, it changes the status of the variable before that change in the code is reached.
In this variant, where nothing has changed except the addition of var
to the line within the if
, the variable switches to undefined
two lines previously, and the if
is now evaluated:
someVar = function(){}; // or, window.someVar =...
(function(){
console.log("before", someVar, typeof someVar);
if(typeof someVar == 'undefined'){
var someVar = true; // <<<<< the only change is adding var here
}
console.log("after", someVar, typeof someVar);
})();
The console shows the variable to now be undefined
before the if
, and switched by the if
which is now evaluated.
Edit: Turns out the same thing happens even if the var
statement can never happen:
neverVar = function(){}; // or, window.neverVar ...
(function(){
console.log("never-before", neverVar, typeof neverVar);
if(false){ // never do this
var neverVar = "changed";
}
console.log("never-after", neverVar, typeof neverVar);
})();
I didn't think it was possible for JS code to influence the execution of code before it's been reached - but clearly the presence of var
later on in the code is somehow stopping the whole scope from looking globally.
Judging by jsbin's warnings, whether or not it is considered out of scope depends on whether there is a var
statement in the scope, even if that var
statement hasn't been evaluated or would not be expected to be evaluated.
Can someone explain this?
(side question, out of curiosity, are there any other examples of Javascript execution being influenced by the mere presence of unevaluated code?)
Upvotes: 1
Views: 41
Reputation: 349032
It is called variable hoisting, where variable and function declarations are processed before the rest of the code is actually executed.
Whenever a var
is encountered in a non-global scope, a new variable is declared and initialized at undefined
in that scope. Since if
does not introduce a new (block) scope, var
inside if
shadows the var
from the parent scope.
Bonus: regarding your side question whether there are any other examples of JavaScript execution being influenced by unreachable code: Yes, but only in terms of performance. Modern JavaScript interpreters are quite sophisticated and try to optimize the interpreted code as much as possible. There are some constructs that result in a performance hit, even if it is within an unreachable code block. For example:
for (var i=0; i<100000000; ++i) ; // Busy loop
if (false) {
eval(); // BOOM - Killed optimizations.
}
Check out the benchmark: The snippet containing eval
runs significantly slower than the one without: http://jsperf.com/eval-hurts-performance.
Upvotes: 3