Block-level function declarations Web Legacy Compatibility bug

# Kevin Gibbons (9 years ago)

In ECMAScript 2015, functions declared at the top level of function bodies and of scripts are var-like and functions declared in blocks are let-like (visible only in that block), generally speaking. This breaks some previously common patterns using block-level function declarations, which were not a part of ECMAScript 5 but were commonly supported in browsers, albeit with varying semantics. Thus, the ECMAScript 6 spec has an extension (annex B.3.3) which is intended to minimize this breakage by allowing function declarations in blocks to also be var-like when possible. This means that they create an additional binding in the containing function's scope which is assigned when execution reaches the block function declaration.

However, B.3.3 looks for functions that permit these additional bindings among the containing function's VarScopedDeclarations, which essentially consists of top-level function declarations and any var declarations. Importantly, it does not include any function declarations inside of blocks, and so will never be applied.

Unfortunately, the function body's LexicallyScopedDeclarations will also fail to contain function declarations inside of blocks. No existing static semantic rule will include the desired function declarations; changing the spec to accommodate this may require substantial additions beyond the scope of this email.

Also, I observe that these additional compatibility steps are only performed for function bodies, not scripts. I'm not sure if this is intentional, but it's a bit counterintuitive: (function(){ { function f(){} } f(); })(); continues to work with no reference errors, but { function f(){} } f(); does not.

# Allen Wirfs-Brock (9 years ago)

Good catches...

On Jul 17, 2015, at 5:12 PM, Kevin Gibbons wrote:

In ECMAScript 2015, functions declared at the top level of function bodies and of scripts are var-like and functions declared in blocks are let-like (visible only in that block), generally speaking. This breaks some previously common patterns using block-level function declarations, which were not a part of ECMAScript 5 but were commonly supported in browsers, albeit with varying semantics. Thus, the ECMAScript 6 spec has an extension (annex B.3.3) which is intended to minimize this breakage by allowing function declarations in blocks to also be var-like when possible. This means that they create an additional binding in the containing function's scope which is assigned when execution reaches the block function declaration.

However, B.3.3 looks for functions that permit these additional bindings among the containing function's VarScopedDeclarations, which essentially consists of top-level function declarations and any var declarations. Importantly, it does not include any function declarations inside of blocks, and so will never be applied.

Unfortunately, the function body's LexicallyScopedDeclarations will also fail to contain function declarations inside of blocks. No existing static semantic rule will include the desired function declarations; changing the spec to accommodate this may require substantial additions beyond the scope of this email.

Right, see fix in ecmascript#4427

Also, I observe that these additional compatibility steps are only performed for function bodies, not scripts. I'm not sure if this is intentional, but it's a bit counterintuitive: (function(){ { function f(){} } f(); })(); continues to work with no reference errors, but { function f(){} } f(); does not.

See fix in ecmascript#4428