1

I don't understand why the value of foo is undefined in the following code:

var foo = 1;

function bar() {
    if (false) {
        var foo = 10;
    }
    alert("Foo is " + foo);
}

bar();

Result : Foo is undefined. https://jsfiddle.net/yk7ae9b0/

Whereas these variations behave as expected.

var foo = 1;

function bar() {
    if (true) {
        var foo = 10;
    }
    alert("Foo is " + foo);
}

bar();

Result : Foo is 10

var foo = 1;

function bar() {
    if (false) {
       // var foo = 10;
    }
    alert("Foo is " + foo);
}

bar();

Result: Foo is 1

Any help will be really appreciated.

Giuseppe Pes
  • 7,772
  • 3
  • 52
  • 90
  • Scoping and hoisting. I will let someone else take a stab at this, but it has to do with the fact you are saying `var foo = 10` instead of `foo = 10` when you have already declared the variable at the top. I can't explain how the javascript just in time engine works or WHY this happens, just that it does. So, if you aren't concerned with the technical explaination just change `var foo = ` to `foo =` inside your if statement. This isn't an answer to the question though. – Ray Suelzer Apr 03 '15 at 19:00
  • 1
    Spelled out here: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html – j08691 Apr 03 '15 at 19:01
  • Look at your question some more, I bet that the JIT compiler is doing some optimization around the always true and false statements that causes some odd behavior! – Ray Suelzer Apr 03 '15 at 19:01
  • 2
    @RaySuelzer nope - `var` only works in function scope, theres no block scope in js. – Daniel A. White Apr 03 '15 at 19:03
  • In ES5 ;) http://ariya.ofilabs.com/2013/05/es6-and-block-scope.html – Ray Suelzer Apr 03 '15 at 19:06
  • My example comes from that article but there is a subtle difference, my function uses `false` instead of `!foo`. Even though `!foo` is always false, it changes the function behaviour as the value of `foo` is 10. – Giuseppe Pes Apr 03 '15 at 19:06
  • !foo is not always false. What if foo were set to false someplace else? – Ray Suelzer Apr 03 '15 at 19:07
  • 2
    @GiuseppePes The short answer to this is that variable declaration with `var` happens before any code executes. So, when it's surrounded by `if (false)`, the `var foo` part is still evaluated and `foo` is created anew, but never gets assigned the value of 10, so the program prints undefined. – wavemode Apr 03 '15 at 19:09
  • I don't think this question should be marked as duplicated. I understand that the other question is related, but it doesn't cover exactly the same case. – Giuseppe Pes Apr 03 '15 at 19:21
  • This question reads like the "Javascript Variable Hoisting" articles, but reversed – Steven Lu Apr 03 '15 at 20:34

1 Answers1

6

Your var foo is not defined where you think it is. Variables in JavaScript are scoped to the function that uses them, not to the block where they are declared. As such, the code you had:

function bar() {
  if (false) {
    var foo = 10;
  }
  alert(foo);
}

Is actually interpreted by the JS engine as if it were written as:

function bar() {
  var foo;
  if (false) {
    foo = 10;
  }
  alert(foo); // foo was never assigned a value!
}

So, when the alert triggers, you have an undefined variable foo that shadows the global foo you declared outside your function.

(This also affects things like for(var i=...) ... -- that var i is function scoped, and will be a declared variable everywhere inside the function, not just inside your for-loop)

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • A way to get around this would be using `foo || window.foo` instead of just `foo`. The one problem with that is _any_ falsy value (including 0 or an empty string) in `foo` will be ignored and `window.foo` is used instead. CoffeeScript adds an existential operator for this reason (`?` vs. `||`), which explicitly checks for `undefined` and `null`. If falsy values are going to come up for you, you can manually do the same. – Pluto Apr 03 '15 at 21:02
  • I'd recommend against that. Never rely on `window`, or really, never rely on global scope: if you needed your original `foo`, pass it into your function call. In this case, they would just call `bar(foo)` with the function definition having a signature `function bar(foo) { .. }`. – Mike 'Pomax' Kamermans Apr 03 '15 at 21:03