On Dec 11, 2012, at 2:45 AM, Andreas Rossberg <[email protected]> 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' ... }
and
{ 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:
http://gyazo.com/df93d0944dff0d9487b81c3cf6802e92
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.
Dave
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss