31

I mistakenly wrote a re-declaration of an argument as a const in a function and instead of throwing SyntaxError: Identifier 'bar' has already been declared I ended up with ReferenceError: bar is not defined..

What causes this behaviour? It wasn't the expected error, and left me confused for a few minutes.

Example code:

function foo(bar) {
  try {
      console.log(bar);
      const bar = 123;
  } catch(err) { console.log(err) }
}
foo(456);

If I don't wrap the declaration in a try/catch, I get (what I believe to be) the expected error.

TylerH
  • 20,799
  • 66
  • 75
  • 101
rlemon
  • 17,518
  • 14
  • 92
  • 123
  • 7
    [Temporal dead zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone_and_errors_with_let) –  Jan 17 '18 at 15:53
  • 1
    It's not a redeclaration if it's not in the same (block) scope. – Bergi Jan 17 '18 at 17:04
  • 1
    useful to understanding is this answer :) https://stackoverflow.com/a/31222689/125981 see who wrote that one :) – Mark Schultheiss Jan 17 '18 at 18:07

4 Answers4

19

Constants are block-scoped, much like variables defined using the let statement.

From this MDN article.

Since you wrapped bar inside a block of braces, its definition is relative to that block. And because you have another bar declaration inside of that block, despite being after the call to it, the compiler will attempt to use this newly defined bar instead of the passed-in parameter. Rename them as separate parameters to mitigate confusion, since one can assume they are holding different data because of your declaration.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Marcus Parsons
  • 1,714
  • 13
  • 19
9

It's a myth that const and let aren't hoisted at all. They're half-hoisted. :-) That is: Within a block, if const bar or let bar (or class bar { } for that matter) appears anywhere in that block, then bar cannot be used prior to that declaration in the block — even if it exists in a containing scope. This area between the beginning of the block and the declaration is called the Temporal Dead Zone:

function foo(bar) {
  // `bar` is fine here
  try {
      // Temporal Dead Zone, `bar` cannot be used here
      console.log(bar);
      // End of TDZ
      const bar = 123;
      // `bar` would be fine here
  } catch(err) { console.log(err) }
}
foo(456);    
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
8

Because const declarations are block scoped, the const bar declaration gets hoisted to the start of the block. (Just after try {)

That means that bar isn't actually defined when you try to log it, because the hoisting overrides / hides the parameter bar.

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • I guess that makes some sense. Confused the crap outta me tho when I saw the error. doesn't seem intuitive (to me at least) when trying to figure out where I effed up. – rlemon Jan 17 '18 at 15:44
  • Yea, I had to look at it twice, as well. – Cerbrus Jan 17 '18 at 15:48
3

Taken from here https://tylermcginnis.com/videos/var-let-const/

var is function scoped and if you try to use a variable declared with var before the actual declaration, you'll just get undefined. const and let are blocked scoped and if you try to use a const or let variable before the declaration you'll get a reference error.

mike123
  • 1,549
  • 15
  • 23
  • but it is already defined in the argument list. remove the try/catch and you get a SyntaxError. So is the try/catch block ignoring the argument list at that point? is that expected? – rlemon Jan 17 '18 at 15:41
  • I guess it is. block scope bit me for the first time :P – rlemon Jan 17 '18 at 15:47