On dropping @names

David Herman dherman at mozilla.com
Wed Dec 26 16:50:28 PST 2012

On Dec 11, 2012, at 2:45 AM, Andreas Rossberg <rossberg at google.com> wrote:

Late to the party, sorry. First, let's be clear there are a couple possible semantics that could be called "temporal dead zone." At this point I'm not sure what various people had in mind; I suspect different people may have understood it differently.

* Read-before-assignment error (TDZ-RBA):

Any read of the variable before it has been assigned throws an error. It can be assigned anywhere, either in the declaration's initializer or in any other assignment expression. If control executes a declaration without an initializer, it leaves the variable in the uninitialized state.

* Use-before-initialization error (TDZ-UBI)

Any read or write of the variable before it has been assigned by its initializer throws an error. It can only be initially assigned by its initializer. If control executes a declaration without an initializer, it leaves the variable initialized to `undefined`.

> The question, then, boils down to what the observation should be: a
> runtime error (aka temporal dead zone) or 'undefined'. Given that
> choice, the former is superior in almost every way, because the latter
> prevents subtle initialisation errors from being caught early, and is
> not an option for most binding forms anyway.

You only listed good things (which I agree are good) about TDZ, but you don't list its drawbacks. I believe the drawbacks are insurmountable.

Let's start with TDZ-RBA. This semantics is *totally untenable* because it goes against existing practice. Today, you can create a variable that starts out undefined and use that on purpose:

    var x;
    if (...) { x = ... }
    if (x === undefined) { ... }

If you want to use let instead, the === if-condition will throw. You would instead have to write:

    let x = undefined;
    if (...) { x = ... }
    if (x === undefined) { ... }

Not only does that look superfluous to existing JavaScript programmers, since they never had to write that out before, but *their code will be rejected by JSHint*. That's actually flagged as bad practice. We cannot and must not introduce new constructs that *require* programmers to use idioms that have already been rejected by the community.

OK, so now let's consider TDZ-UBI. This now means that an initializer is different from an assignment, as you say:

> They are initialisations, not assignments. The difference, which is
> present in other popular languages as well, is somewhat important,
> especially wrt immutable bindings.

For `const`, I agree that some form of TDZ is necessary. But `let` is the important, common case. Immutable bindings (`const`) should not be driving the design of `let`. Consistency with `var` is far more important than consistency with `const`.

And for `let`, making initializers different from assignments breaks a basic assumption about hoisting. For example, it breaks the equivalence between

    { stmt ... let x = e; stmt' ... }


    { let x; stmt ... x = e; stmt' ... }

This is an assumption that has always existed for `var` (mutatis mutantum for the function scope vs block scope). You can move your declarations around by hand and you can write code transformation tools that move declarations around.

It's certainly how I understand hoisting in JavaScript, and it's how I describe it in my book:


In fact, Doug has taught countless programmers to hoist their declarations to the beginning of their function, and many (perhaps including Doug?) will probably do the analogous this with let, manually hoisting them to the beginning of the block. This transformation will actually defeat the error checking, since the variables will then become initialized to `undefined`.

I imagine your reply is: don't do that transformation; place your `let` declarations as late as possible before they are going to be used. I guess I need to be convinced that the equivalence is of no value.


More information about the es-discuss mailing list