1

Why does var APP = APP || {}; work fine, but const APP = APP || {}; not? Or let APP = APP || {} for that matter.

Uncaught ReferenceError: APP is not defined

As this only has to do with the evaluation of APP and not anything of what it is set to.

Afs35mm
  • 549
  • 2
  • 8
  • 21
  • 2
    Well const can only be declared once so using that pattern seems wrong. – epascarello Oct 27 '17 at 19:33
  • 1
    Maybe https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6 – epascarello Oct 27 '17 at 19:36
  • What do you mean by "work fine"? What is this code supposed to be doing? – melpomene Oct 27 '17 at 19:39
  • 2
    `||` is JavaScript's **logical OR** – it can be used to coalesce *falsey* values, but it's not a null-coalescing operator. – Mulan Oct 27 '17 at 19:42
  • It's most likely because with `var` it's actually `var App=undefined;APP = APP || {}` due to var hoisting. Do the same with `let` and it would work too. – Keith Oct 27 '17 at 19:52

2 Answers2

10

Why does var APP = APP || {}; work fine, but const APP = APP || {}; not?

Lets clarify how these statements are evaluated. Before any code is executed, the runtime finds all variable declarations and creates an entry for each in the current scope. Some time later when APP = APP || {} is executed, the value of APP is read before it was ever assigned value.

It "works" with var, because var declarations are implicitly initialized with the value undefined. Therefore accessing the variable before a value is assigned to it returns undefined.

const and let declarations however are left uninitialized. You cannot access them before they have been assigned the value from their initial declaration. This is also known as temporal dead zone or TDZ for short.

Here is a simplified example:

console.log(foo); // undefined
var foo = 42;
console.log(foo); // 42

vs

console.log(foo); // ReferenceError because of TDZ
let foo = 42;
console.log(foo); // 42 in theory, but this line is never executed due to the error above
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thank you for being literally the only person to answer the question. Follow up: Since the pattern above has been so prevalent for so long until webpack et al came along, and technically declaring a variable/namespace/whatever is still used, how would one alter to achieve the same effect but using `const`/`let` – Afs35mm Oct 27 '17 at 20:24
  • 1
    I think it depends on the context. Honestly `var APP = APP || {};`. Doens't make much sense to me. Either `APP` is **global** in which case you should be using `window.APP = window.APP || {};`, or `APP` is **local** in which case know already that `APP` is not defined, and should just be doing `var APP = {};` (or `let` / `const`). – Felix Kling Oct 27 '17 at 20:45
1

From MDN:

In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.

The same applies for const:

All the considerations about the "temporal dead zone" apply to both let and const.

In other words, the reason this works:

var APP = APP || {};

is because of variable-hoisting which translates the above to:

var APP;          // <-- declares APP
APP = APP || {};  // <-- initializes APP

and since variable-hoisting does not apply to either let or const:

const APP = APP || {}; // <-- Fails because APP is not yet initialized.
let APP = APP || {};   // <-- Fails for the same reason.

Finally, for the record (as rightly pointed out by naomik), the || operator is not a null-coalescing operator but rather a short-circuit OR operator.

The confusion over this is likely because of statements like this:

var n = n || '';

For the above statement, there are two things to bear in mind with these.

  1. n is not null... it is undefined (which is different from null).

  2. Because of variable-hoisting, that statement is the equivalent of:

    var n;        // n has the value of undefined
    n = n || '';  // the comparison is now: undefined || '';
    

and since undefined is a "falsy" value, the short-circuit OR operator returns the second value.

This behavior holds true for null as well, because null is also "falsy".

pete
  • 24,141
  • 4
  • 37
  • 51
  • *"Fails because APP is not yet declared."* It's a little bit more complicated than that (and MDN could be more precise about this). The variables do exists in the scope but are left *uninitialized*. That's what causing the reference error. We can kind of prove this doing `var foo = 42; (() => { console.log(foo); const foo = 21;})())`. – Felix Kling Oct 27 '17 at 20:25
  • Good catch! (I meant to type "uninitialized", but my head was stuck on "declared") Updated answer. – pete Oct 27 '17 at 20:35