2

While coding in a Node.js REPL, I accidentally declared a new symbol by using the new keyword and since Symbol() is not a constructor, I got an error as shown in this snippet,

let sym = new Symbol("some description");
// TypeError: Symbol is not a constructor
//     at new Symbol (<anonymous>)

So I thought that's fine, I'll do it the right way but I got another error.

let sym = Symbol("some description");
// SyntaxError: Identifier 'sym' has already been declared

That was strange to me because I expected that since my declaration caused an error, the variable has not been defined at all but now I see it seemingly is defined. My next guess was that maybe it got an undefined value, so I tried to reach to this variable and see what value it owns.

console.log(sym);
// ReferenceError: sym is not defined

WHAT? So the variable sym is both defined and undefined?

I even tried to use the block scope of an object so that I can check whether the variable does actually exist in the object using the Object.prototype.hasOwnProperty() function but unfortunately, the same problem emerged and the whole object became undefined and inaccessible.

let obj = {
  a: 4,
  b: new Symbol("some description"),
};
// TypeError

console.log(obj);
// ReferenceError

let obj = {};
// Syntax Error

I found that this issue could go away if I use var declarations, so I assume it has something to do with block scopes that I don't know.

So I have 3 questions with respect to this problem:

  1. Where is the variable sym defined and where is it undefined? Any connection between this case and the idea of "Temporal Dead Zone"?

  2. What value does sym own?

  3. How can I assign a new value to the sym variable?

  4. Is this problem specific to this case or any erroneous let/const declaration will result in the same issue?

Saeed Ahadian
  • 212
  • 2
  • 4
  • 14
  • 1
    Yeah, this behavior is a weird result of using REPLs, which use a *single scope* on the top level over multiple asynchronous calls. `let someVarName = ` puts `someVarName` into the Lexical Environment, and `let` variables can't be re-declared later. But if `` throws, the variable name basically can't be used again – CertainPerformance Dec 17 '19 at 11:03
  • Your `sym` variable name remains in the Temporal Dead Zone forever (at least until you restart the REPL) – CertainPerformance Dec 17 '19 at 11:03
  • @CertainPerformance not entirely inconsistent with how real code runs. Only you can't run any more commands after a failed expression. So, it's just GIGO in motion, I suppose. – VLAZ Dec 17 '19 at 11:04
  • Yeah, usually, if assignment to a variable fails, the fact that the current scope now has an inaccessible variable in the TDZ doesn't matter, because an error was thrown, so further statements below in the same scope won't ever get to run. But in a REPL, or at least in Node and browser consoles, that's not the case - you *can* continue to write code that gets executed in the same scope (which will throw if the perma-TDZ'd variable ever gets referenced or defined again) – CertainPerformance Dec 17 '19 at 11:08
  • Sure, it's annoying but then again as I said - garbage in, garbage out. The simple solution is to clear the scope every time which is not desirable. Having a manual way to clear scope is basically a repeat functionality of restarting the REPL. And if you have to write a lot of code to get back to the same state, then you should probably be writing and executing a script file. Also, you can just use `var` when declaring stuff in a REPL - that's what I do. So, failed bindings screw up the environment but there are outs of it already, no real need to implement some environment cleaning. – VLAZ Dec 17 '19 at 11:12

0 Answers0