8

See this code:

<script>
let {foo} = null; // TypeError
</script>
<script>
// Here I want to assign some some value to foo
</script>

The first script attempts to let-declare foo via a destructuring assignment. However, null can't be destructured, so the assignment throws a TypeError.

The problem is that then the foo variable is declared but uninitialized, so if in the 2nd script I attempt to reference foo, it throws:

foo = 123; // ReferenceError: can't access lexical declaration `foo' before initialization

And let variables can't be redeclared:

let foo = 123; // SyntaxError: redeclaration of let foo

Is there any way to take it out of the TDZ, so that I can assign values and read them?

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • BTW, I want to use `foo`, not workarounds like `window.foo`. – Oriol Apr 15 '16 at 16:20
  • I'm guessing there's something interesting about the first script that makes assuring the validity of the initialization difficult. – Pointy Apr 15 '16 at 16:21
  • @Oriol: `window.foo` [wouldn't work anyway](http://stackoverflow.com/q/28776079/1048572) :-) – Bergi Apr 15 '16 at 16:28
  • 1
    What environment are you executing this in (browser, version number)? How mature is its ES6 support, could it be that this is just a bug? – Bergi Apr 15 '16 at 16:29
  • @Bergi well I'm not sure what the runtime can do about it. The TDZ in the global context has always seemed weird to me. I guess it'd be better if the exception in the first script block would cause the symbol to be removed as if there'd been no `let`, but that seems kind-of hackish because of hoisting. – Pointy Apr 15 '16 at 16:33
  • @Bergi I tried Firefox and Chrome. And seems reasonable: a variable is in the TDZ between its declaration and its initialization. Here the initialization doesn't complete due to the error, so the variable remains in the TDZ. But if my reasoning is wrong and this is a bug, it would be a possible answer. – Oriol Apr 15 '16 at 16:34
  • 2
    I just looked through the spec and this behaviour seems correct. I also found a third reasonable attempt which doesn't work either: `delete foo;` – Bergi Apr 15 '16 at 17:01
  • You might want to post this to `es-discuss`, to spark a discussion whether this behaviour is intended and what could be done about it. Also, an example that doesn't rely on destructuring: `let foo = (() => {throw;})();` – Bergi Apr 15 '16 at 17:04
  • @Bergi Yes, the destructuring was only a short way to throw an error during the assignment. Another example is `let foo = null.throw`. I will try es-discuss. – Oriol Apr 15 '16 at 17:20
  • I [posted this to es-discuss](https://esdiscuss.org/topic/take-let-variable-out-of-temporal-dead-zone). It had already been discussed in [Global lexical tier](https://esdiscuss.org/topic/global-lexical-tier) – Oriol Apr 16 '16 at 12:54

1 Answers1

2

It's impossible. Temporal dead zone and restricted access to uninitialized let variable are expected to be unavoidaible. It's confusing and problematic, but intended and expected.

See spec for details:

NOTE let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.\

So if variable wasn't initialized on declaration (and throwing before initialization obviously result in no initialization) it can't be accessed by any means.

But in fact, your issue is more complex than throwing assigment. It's architecture issue - you are depending on mutable global variables. It's big "no no no" and you should refactor your code to use explicit dependencies.

Ginden
  • 5,149
  • 34
  • 68