Hoisting behaviour of 'const' and 'let'

David-Sarah Hopwood david.hopwood at industrial-designers.co.uk
Sun Oct 12 19:31:30 PDT 2008

YR Chen wrote:
> On Sun, Oct 12, 2008 at 5:27 PM, David-Sarah Hopwood <
> david.hopwood at industrial-designers.co.uk> wrote:
>> Alternatively, if the program is completely known in advance, then it
>> can be fully processed for function declarations, but in that case it is
>> statically invalid because f is referenced outside its scope.
> My example assumes that this is in a single script, so the function
> definition is hoisted to the top. But you're not still not following the
> example properly. How can a syntax analyzer determine that f is being
> referenced in that example? That is f is being recalled as a property
> of the global object.

Ah, I see your point now (it was obviously too early in the morning for me
when I answered before).

It is possible to fix this by only setting the 'f' property at the start
of f's effective scope.

That is, for each function:
 - find the point just after the textually last declaration of a 'const'
   or 'let' variable that the function refers to and that is declared
   in the same block.
 - if not all of the other 'const' and 'let' variables that the function
   refers to are in scope at that point, then the function's effective
   scope is empty (and it can be optimized out);
 - otherwise, generate an assignment of the function value to the
   function's identifier [*1] at that point.

(It is not necessary to delete the property after the scope of f
finishes, since if the start of its scope is reached, we know that
all 'const' and 'let' variables it refers to have already been assigned.)

This is slightly more complex than I'd hoped, but I think the guarantee
that it is never possible to see uninitialized values of 'const' or 'let'
variables is worth it.

To anticipate a possible objection: yes, you can now see an undefined
value for the function identifier in a case where that would not have
happened under the old semantics [*2] -- but that can only happen for
functions defined in the global scope that use 'const' or 'let' and
are accessed dynamically; and no read or write barriers are required
even in that case.

[*1] The scoping issue only arises for function declarations since
     only those are hoisted; it does not arise for anonymous function
[*2] But there is no backward incompatibility with ES3, because only
     functions that use 'const' or 'let' are affected.

> That is, Given the following:
> // y is dynamically set to 'f'
> g = this[y];  // g is now f
> g();
> const x = 10;
> function f() { ... x ... }
> how can you determine that g refers to f via static analysis?

You can't, but you don't need to. At "this[y]", f is not in scope so it
would not have been assigned to "this['f']" yet.

I.e. the code generated is something equivalent to:

// y is dynamically set to 'f'
g = this[y];
const x = 10; f = $f;  // <- inserted at start of f's effective scope
function $f() { ... x ... }

where '$f' is not accessible to the program.

David-Sarah Hopwood

More information about the Es-discuss mailing list