4

I'm reading Kyle Simpson: ES6 & Beyond and I am at the second chapter. In the section titled "Block-Scoped Functions" an example is given:

{
    foo();                  // works!

    function foo() {
        console.log("works!");
    }
}

foo();                      // ReferenceError

(the console.log was added by me for testability.)

I would expect this code to run foo successfully at the first call, even though the function is defined below the call and to crash at the second call due to ReferenceError, as the book is claiming. However, if I run this in Chromium (Version 71.0.3578.98 (Official Build) snap (64-bit)) both function calls are successfully executed and "works!" is outputted twice to the console. If I run the same script in FireFox, then the first function call does not log anything and the second function call, outside the scope is outputting "works!". So, Chromium and FireFox behave differently from each-other and from the expectations drawn from the book. I have transpiled this in babeljs.io and the result was:

"use strict";

{
    // works!

    var _foo = function _foo() {
        console.log("works!");
    };

    _foo();
}

foo(); // ReferenceError

Strangely, foo is defined as function scope, (the keyword var is used instead of the keyword let or const), so it is reachable from outside the scope, yet the call outside the scope was not changed to _foo, hence we will have an error, but if we change the script to

{
    foo();                  // works!

    function foo() {
        console.log("works!");
    }
}

window["foo"]();                        // ReferenceError

the transpiler does not change the name of foo to _foo. With all these experiments I have reached to the conclusion that if I intend to have a block-scoped function, I need to define it explicitly, like:

{
    foo();                  // works!

    let foo = function() {
        console.log("works!");
    };
}

foo();                      // ReferenceError

but then even the first call for foo crashes due to temporal dead zone issue. So, my question is: Why does a block-scoped function work outside the scope?

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • 2
    The chapter you're looking at explains the situation. Prior to ES6, implementations varied. In "strict" mode, both Firefox and Chrome exhibit the ES6 behavior of scoping the function strictly to the declaring block. – Pointy Dec 24 '18 at 13:41
  • @Pointy you seem to be right, I have tested it and the behavior was according to the book. However, the book does not mention the need for strict mode, hence the confusion. If you convert this into an answer, I will accept it. – Lajos Arpad Dec 24 '18 at 13:46

1 Answers1

1

As Kyle's book mentions, the semantics of function declarations in blocks has in the past varied between implementations. (That may explain why declaring functions inside blocks was generally discouraged in coding style commentaries.)

Browsers generally want as much old code to work as possible, so with a fairly important change like not hoisting a block-nested function declaration to the containing function scope old code could certainly break. Thus, for lack of a better alternative, the "use strict"; flag turns the ES6 semantics "on". Old code (probably) won't have "use strict"; anywhere, so it'll continue to behave the old way (which of course has always been risky).

Pointy
  • 405,095
  • 59
  • 585
  • 614