4

I'm trying to get my head around scoping. From my understanding ES5 does not have the ability to block-scope, therefore, inner f() should be in scope of the body test.

These are the results from running the function

  • test(true); // ["local", "local"]
  • test(false) // f is not defined

However, I cannot understand why test(false) gives an error? I was expecting - [local].

  function f() { return "global"; }

    function test(x) {
        var result = [];
        if (x) {
            function f() { return "local"; } // block-local

            result.push(f());
        }
        result.push(f());
        return result;
    }
stckpete
  • 571
  • 1
  • 4
  • 13
  • [A very similar question.](http://stackoverflow.com/q/10069204/417562) – lonesomeday Dec 14 '16 at 18:08
  • 1
    Ha! If you 'use strict' mode in Chrome 54, the results are ["local", "global"], ["global"]. So it seems it fixes things by creating scope within the `if`. I don't think this should be allowed, hence the inconsistent results. Otherwise this is a bug in the browser, since by my understanding the function should be available throughout the entire function and not effected by the `if`. – Daniel Gimenez Dec 14 '16 at 18:11

2 Answers2

3

This is what your code actually looks like when it is compiled:

function f() { return "global"; }

    function test(x) {
        var result = [];
        var f; 
        if (x) {
            f = function () { return "local"; } // block-local

            result.push(f());
        }
        result.push(f());
        return result;
    }

The f variable (the variable holding the function pointer) is declared at the top of the function, but it is defined within the conditional.

JavaScript also has a feature called: strict mode. This was put in after the original development of the language to enforce some best practices and some rules to keep developers from making incorrect assumptions about how they think the language works based on other languages with a similar syntax. In this instance, strict mode actually changes the scoping of the new f function.

The following code:

function f() { return "global"; }

    function test(x) {
    "use strict" // STRICT MODE TAG INSERTED HERE
        var result = [];
        if (x) {
            function f() { return "local"; } // block-local

            result.push(f());
        }
        result.push(f());
        return result;
    }

test(true);
test(false);

produces the following results result:

["local","global"] //test(true);

["global"] //test(false);

It overrides the default scoping rules and makes sure that the new declaration of f only applies to within the conditional where it was defined.

kemiller2002
  • 113,795
  • 27
  • 197
  • 251
0

This is because when your condition is true, 'f' is a function declaration ,so it checks for the inner f function .

When x is false,as 'f' is not visible it throws an error and it cannot see the global 'f' function because f is already overridden in the local

To override that you can either explicity call the window.f() or make the f() inside the if condition an IIFE

check these snippets

function f() {
  return "global";
}

function test(x) {

  var result = [];
  if (x) {

    // block-local
    result.push((function f() {
      return "local";
    })());
  }
  result.push(f());
  return result;
}
var res = test(true);
console.log(res);
var res1 = test(false);
console.log(res1);

Hope it helps

Geeky
  • 7,420
  • 2
  • 24
  • 50