In this proposed alternative to sloppy-mode function hoisting semantics, sloppy-mode FunctionDeclaration are always single var
-style bindings. The var
is set to the value of the function at the top of the block where the FunctionDeclaration occurs. I believe these semantics roughly match IE10 and earlier sloppy mode function hoisting semantics.
- Maintain compatibility at the intersection of what browsers have supported for a long time
- Support is self-defining functions defined in blocks (tc39/ecma262#162)
- If possible, be intelligible for users and implementors (simple semantics win out when they make sense)
- Preserve strict-mode lexical scoping of FunctionDeclarations (this proposal only affects sloppy mode)
- Make a lexical binding in sloppy mode, and generally try to make sloppy mode block-scoped FunctionDeclarations act like lexical bindings, including suppressing errors, when possible
To give an example in terms of imprecise desugaring, the following declaration would turn into the next code:
(function() {
A
if (foo) {
B
function bar() { }
C
}
D
})();
would have semantics that are something like this:
(function() {
var bar;
A
if (foo) {
bar = function() {}
B
C
}
D
})();
so B, C and D would experience the new binding. In this sense, the proposal is just like Annex B 3.3. However, the difference is that only a single binding is created, rather than two in Annex B 3.3, a lexical and var
-style binding.
Implementing this change in the ECMAScript specification will be rather cross-cutting. For this reason, I think it would be impractical to do within Annex B, and it would be better to think of it together with the rest of strict/sloppy semantics. (I'd suggest that we reconsider the split of Annex B from the rest of the spec--Annex B together with earlier parts of the ES spec are inseparably both supported by all usable ECMAScript implementations.)
Parts of the spec that would need to be changed:
- BlockDeclarationInstantiation step 2.b. starts with a strict/sloppy mode check. In strict mode (or with generators, which maintain a single lexical binding), it maintains its current behavior; in sloppy mode, it sets the variable binding for a FunctionDeclaration contained in its block to the value defined in the block.
- FunctionDeclarations in sloppy mode should be in VarScopedDeclarations rather than LexicallyScopedDeclarations even if they are in a block, and similarly, should be in LexicallyDeclaredNames rather than VarDeclaredNames.
- FunctionDeclarationInstantiation, GlobalDeclarationInstantiation and EvalDeclarationInstantiation need to treat sloppy-mode block-scoped function declarations differently from top-level (within that scope) declarations. Namely, rather than choosing the last one and initializing the binding to that, they should initialize the binding to undefined. These algorithms can do the test similarly to how Annex B 3.3 does: "For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, or DefaultClause,"