user56reinstatemonica8
user56reinstatemonica8

Reputation: 34084

Presence of `var` within an `if` influences whether the if is evaluated?

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

Answers (1)

Rob W
Rob W

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

Related Questions