Special casing class/function declarations to have completion semantics
The completion value of class and function declarations are undefined.
eval('class x {}') // undefined
eval('(class x {})') // class x{}
eval('function x() {}') // undefined
eval('(function x(){})') // function x() {}
eval('() => {}') // () => {}
From my understanding, there hasn't been much of a use case for completion values of class and function declarations in the past, but it can be a valid use case as do-expressions become more popular. Consider the following JSX:
<div onClick={do {
if (flag) {
function a() {...}
} else {
function b() {...}
}
}} />
In the above example, the onClick handler will be assigned a value of undefined, which is probably not what the user would intend from writing the code.
Is there a way to special case these so that they have completion semantics? e.g.
eval('class x {}') // class x{}
eval('function x() {}') // function x() {}
There may also be other such declarations with similar concerns.
Trying to disguise function declarations into expressions raises some issues, however.
- The completion value of declarations (and empty statements) is not exactly
undefined, butempty, meaning that the completion value of the preceding statement is forwarded. For example:
"use strict";
(do {
if (true) {
let x = 4;
y = f(x);
y + 2;
function f(x) {
return x*10;
}
}
})
As currently specced, the do-expression evaluates to 42, despite the function declaration. Until proof of the contrary, this is what the programmer intended.
- In your example, people will try to write:
(do {
if (flag) {
function () {...};
} else {
function () {...};
}
})
but this is a syntax error, as the function keyword at the beginning of the statement is always interpreted as starting a function declaration, and function declarations must be named. That said, we could allow unnamed function declaration just for the sake of this case.
I think it would be better to just outright ban function declarations from do expressions, instead treating every function keyword as the start of a function expression. Odds are, people using do expressions are fine with using arrow function for everything, or otherwise using function expressions for the rare case that they need a different this context.
Another option is treating function declarations by different semantics in do expressions, removing block hoisting and treating function declarations as expressions as well. (Essentially the same behavior as let functionName = function functionName() {}).
As for class, the two declarations would have to have identical semantic shifts in do expressions. I prefer outright banning both.
The current proposal bans all declarations, including function and class, at the end of do expressions. That prevents the confusion from arising, but is kind of awkward. I'd be interested in seeing if we can change the completion values for declarations in general, although not necessarily as part of this proposal.