8

I try writing the following lines in the console one by one

let x = y //throws error "Uncaught ReferenceError: y is not defined"
console.log(x) //throws error "ReferenceError: x is not defined"
let x = 3; //gives error "Uncaught SyntaxError: Identifier 'x' has already been declared"
x = 3 //ReferenceError: x is not defined

Now problem is that how can be a variable not defined and has been declared at the same time. Is there any difference between both.

Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
  • 3
    It's a poor use of language by the browser consoles. Those `ReferenceError`s should really say the variable is not "declared", but afaik it's always been thus. – Robin Zigmond Mar 04 '19 at 09:11
  • 3
    From link: *When there's assignment, the right-hand side is parsed first; if the right-hand side throws an error, it never gets to the left-hand side, and the variable declared with let never gets properly initialized; it'll stay in the demilitarized zone / temporal dead zone forever* (and you can't re-declare a variable that's already been declared, even though the attempted assignment during initialization threw an error). – CertainPerformance Mar 04 '19 at 09:12
  • There's a big difference between **declaration** and **initialization**. In your first line `x = y`, you declared `x`and tried to assign `y` to it, which is `undefined`, so `x`is declared and will be initialized with `undefined` as `value`. That's why you got `x` is already declared. – cнŝdk Mar 04 '19 at 09:15
  • @cнŝdk but `console.log(x)` throws an error `x is not defined` – Maheer Ali Mar 04 '19 at 09:16
  • @ CertainPerformance Thanks. Your answer makes sense but if the variable is declared we should be able to assign it a new value but `x = 3` also throws error. Here there is not re-declaration – Maheer Ali Mar 04 '19 at 09:21
  • 1
    @MaheerAli `let x = 3;` will throw error as you have already declared it. But `x=3` should not throw error, unless you define it as `const` – Rajesh Mar 04 '19 at 09:26
  • @Rajesh But `x=3` is throwing error – Maheer Ali Mar 04 '19 at 09:27
  • 1
    Being declared doesn't mean it will be initialized to undefined. [Are variables declared with let or const not hoisted in ES6?](https://stackoverflow.com/a/31222689/3082296) – adiga Mar 04 '19 at 09:29
  • @CertainPerformance quick question on your explanation. `let x` on first line adds `x` to TDZ. Since `y` is not available, it throws error and assignment fails. Hence line 2 and 3 fails. But on line 4, `x=3` should do a proper assignment and it should remove `x` from TDZ. But that also fails. I fail to understand that – Rajesh Mar 04 '19 at 09:42
  • @Rajesh `x` is declared because of the hoisting but it's never initialized. From [Bergi's asnwer](https://stackoverflow.com/a/31222689/3082296) "*The lexically declared variables however stay uninitialised. This means that a ReferenceError exception is thrown when you try to access it. It will only get initialised when the let/const/class statement is evaluated*" Since the line does that initializing it, throws an error here, `x` is in sort of a *limbo* where it's declared but unintialized. So, any type of assignment `x=3` will always thrown an error. – adiga Mar 04 '19 at 10:02

1 Answers1

7

A let or const variable can only be declared once - that is, when you have let <variableName> in a scope, you have declared <variableName> in that scope, and cannot declare it again in that scope.

From the previously linked question:

When there's assignment, the right-hand side is parsed first; if the right-hand side throws an error, it never gets to the left-hand side, and the variable declared with let never gets properly initialized; it'll stay in the demilitarized zone / temporal dead zone forever

You can't re-declare a variable that's already been declared, even though the attempted assignment during initialization threw an error.

But on line 4, x=3 should do a proper assignment and it should remove x from TDZ. But that also fails. I fail to understand that

After a variable has been initialized (for example, the let x runs), it can be assigned to. But just like you can't assign to a variable before its let initialization, you also can't assign to a variable later, when its initialization did not complete successfully:

x = 'foo';
let x = 'bar';

Error:

Uncaught ReferenceError: x is not defined

Which is the same sort of thing that happens in the console when you try:

let x = y
// Uncaught ReferenceError: y is not defined
// x has not been initialized, so the next line throws:
x = 'foo'
// Uncaught ReferenceError: x is not defined

x still has not been initialized, so the error is the same.

Encountering this sort of thing is pretty odd, though - you only see it in the console. In normal scripts, a thrown error will prevent further execution, and the fact that a variable name remains uninitialized forever is not something to worry about.


The above was an issue in earlier Chrome versions. But in Chrome 80+, re-declarations of let are now permitted, so the error

Uncaught SyntaxError: Identifier 'x' has already been declared

should no longer occur, regardless of whether the previous initialization of the variable succeeded or not:

enter image description here

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • 1
    I'd like to point out that in Firefox's console `let x = y` does initialize `x` and `x = "foo"` doesn't throw. However doing the same [out of the console](https://jsfiddle.net/dpm0vu21/), it throws correctly. – Kaiido Mar 05 '19 at 00:48