block scope and function declarations

Allen Wirfs-Brock allen at
Wed Feb 15 09:02:31 PST 2012

On Feb 15, 2012, at 3:55 AM, Andy Wingo wrote:

> Hello again ecmascriptians,
> There has been some discussion about treating function declarations as
> "var" declarations.  While I understand the concerns about
> compatibility, I think it will be surprising to programmers.

Not really,  the intent is that function declarations are lexically scoped to the closest inclosing block (or function body), just like let. The primary difference between let and function is that the initialization of a function binding occurs at the top of the enclosing block

Treating function like var has only been discussed in a couple of contexts.

At the global level function may need to bind like var rather than let for browser legacy compatibility reasons and in support of multiple top-level script blocks.

The other var related issue concerning function scoping also relates to legacy compatibility.  ES5 and earlier does not allow for  function declarations in blocks. However, all browser JS implementations do allow such declarations but they don't agree upon a common semantics.  Some treat such function declarations like var declarations in terms of hoisting.  FF does something that is somewhat similar to let, but not quite the same thing.  In general, use of such function declarations are not interoperable among browsers, but there are some simple usage patterns that will produce the same results in all browsers.

At the last TC39 meeting it was hypothesized that the block nested function declaration patterns that are actually used inoperably on the web would continue to work if interpreted as lexical (let-like) declarations.  On that basis, we decided that we would go forward with let-like scoping of block nested functions for all ES6 code. However, we will also experiment with early implementations to ensure that this does not "break the web".  If it does, we will have to revisit the issue and probably only use let-like scoping for functions within strict mode.

> For example:
>  function f(x) {
>    bar(); // ???
>    for (let y of x) {
>      function bar() { ... y ... }
>      bar(); // OK
>    }
>    bar(); // ??? 
>  }

both var references outside of the for-let block result to their bindings in the scope that surrounds the declaration of f.  If that is the global scope, they probably throw as unresolved references.

> Here I think it's pretty natural to expect that the FunctionBody of
> `bar' has access to lexically scoped binding for `y'.  But outside the
> block, the function bound to `bar' doesn't have any meaning.

outer scope reference

> I only see two consistent answers here:
>  function foo(){}  ==  let foo = function(){}
> or
>  funciton foo(){}  ==  var foo = function(){}
> Hoisting the function definition doesn't make sense with block scope.

All declarations within a block are "hoisted" to the top of the block but can only be validly referenced after they have been initialized.  The span between the top of the block and the point of initialization is called the "temporal dead zone".  Function declarations are initialized at the top of the block.  Let and const declaration are initialized when evaluated in statement list order.


More information about the es-discuss mailing list