Hoisting behaviour of 'const' and 'let'
Brendan Eich
brendan at mozilla.com
Mon Oct 13 14:40:27 PDT 2008
On Oct 11, 2008, at 9:02 PM, David-Sarah Hopwood wrote:
> David-Sarah Hopwood wrote:
> [...]
>> What is proposed to be hoisted in the case of 'const' and 'let' is
>> just
>> the point at which the variable's *scope* begins. But, unlike the
>> case
>> of function definitions, this in general extends the scope to a point
>> where the variable has not been initialized. If 'const' and
>> 'let' (with
>> an initializer) were not hoisted, then there could be a static
>> guarantee
>> that the variable will have been initialized at every point at
>> which it
>> is in scope. By hoisting the scope, we're effectively throwing away
>> the
>> possibility of such a guarantee -- at least if we want to avoid a
>> much
>> more complicated static analysis.
>
> This would also mean that we cannot have any types that do not include
> the value 'undefined' (either primitive types, or "non-nullable"
> reference
> types).
An alternative implemented in some JS variants has a default value
other than undefined, specific to the annotate type of the hoisted
binding. This works well for scalar types. It leads to a value vs.
reference type (struct vs. class in C#) distinction. We considered
taking ES4 that far, but in the end decided not to go all the way,
instead hardcoding the default value for each built-in primitive or
"value type".
If we don't require let initializers then users will sometimes forget
them, and use-before-set errors will happen -- unless we require
analysis or a read barrier to make use-before-set an error. If we do
not mandate analysis, then test coverage gaps will leave some such
errors to be found in the field.
Meanwhile, real code on the web counts on var being default-
initialized to undefined. Swimming upstream against this current will
be hard. Because let (even with a use-before-set hazard) has other
virtues over var, in order to make let the new var and encourage its
widespread use, I do not think we should require let declarations to
have initializers.
[Another point, mostly historical about ES4: changing let and const
not to hoist (when we considered that choice) was not enough to avoid
the "default value" problem in ES4. The ES4 class syntax left instance
variables with non-nullable type annotations in need of default
values, or else error on use-before-set (with attendant read barrier
or static analysis costs). But the harmonized class syntax I proposed
in Oslo avoids at least this case:
class Point(x, y) {
let r = Math.sqrt(x*x + y*y);
let theta = Math.atan2(y, x);
. . .
}
by making the constructor head part of the class head, so the
constructor parameters are in scope for evaluation of the constructor
body, which is the class body.
There are other cases remaining where it's anywhere from awkward to
painful to mandate initialization of every binding. Such hard cases
can all be worked around at some level of discomfort, but var has no
such mandatory initialization burden, so to keep let competitive as
the new var, we passed on requiring initializers.]
My two cents: the argument for let and const not hoisting is strong
enough on its merits, ignoring default value issues. The question of
initial values for uninitialized type-annotated bindings does not
weigh heavily in its favor, as it calls for type-specialized default
value support.
/be
More information about the Es-discuss
mailing list