JavaScript ReferenceError not defined vs SyntaxError already been declared
Let’s talk about JavaScript;
Run this minimal repro in chrome, node, or whereever you like:
let {foo} = null; // TypeError
let foo = 1 // err!
foo = 1 // err!
console.log(foo) //err!
You will see:
ReferenceError: foo is not defined
SyntaxError: Identifier 'foo' has already been declared
In the name of Schrödinger! Does foo exist or not? Live or dead?
Turns out, this is compliant with the spec. This is why the behavior is consistent across implementation.
GetBindingValue(N, S) Returns the value of an already existing binding from an Environment Record. The String value N is the text of the bound name. S is used to identify references originating in strict mode code or that otherwise require strict mode reference semantics. If S is true and the binding does not exist throw a ReferenceError exception. If the binding exists but is uninitialized a ReferenceError is thrown, regardless of the value of S.
Simply put, declaration is hoisted so foo is declared. But it’s not defined/initialized. It’s dead. Can’t be rescued.
Scary thing is that you can use this to forever ban a binding name. Nobody can ever create foo anymore.
Table 14: Abstract Methods of Environment Records (It’s very ironic that the link says table-15, which reflects on the kind of language JavaScript is)