JS Behind The Scenes : Hoisting and the TDZ – Temporal Dead Zone

Loading

Hoisting by the JS Engine makes some variables available for use before they are declared by the JS engine creating a "variable environment object" as the code is initially scanned before being run.

It is best to experiment with the various variable type to understand which get hoisted and which don't, as behaviour varies markedly.

HOISTED? Initial Value scope

 

Function declarations Yes Actual function block
var Yes undefined function
Let/const Not in practice, technically Yes Uninitialized,

Temporal Dead Zone

block
Func expressions/Arrows Depends if var or let/const used
// Temporal Dead Zone, Let and Const: Function Declarations (function blah = {} etc) are hoisted 
const myName = 'Jonas'

// var "job" is defined but inaccesible - and VS code does not highlight ${} as a var either
if (myName === 'Jonas') {
  // -------------TDZ for job var-----------
  console.log(`${job}`);
  const job = "teacher"
  // Uncaught ReferenceError: Cannot access 'job' before initialization - because of unhoisted variable const - if lines are reversed (Alt ^ arrow) then it works. Initially it is treated as not existing

  // const age = 2037 - 1989
  // console.log(age);
  // 48
  //--------------TDZ for job var-----------
  
  // 
  //-------------x is in TDZ until initialised _________
  // console.log(x)
  // Uncaught ReferenceError: x is not defined yet - it is in TDZ as uninitialized from JS initial code scan, so it is expected to be defined at some future point
 //-------------x is in TDZ until initialised _________


So what is and why a TDZ? It was created in the JS Engine so that variables would not be accessible before they are declared to prevent bugs, bad programming practises and catch errors. So the JS engine in the browser F12 console flags an error and prevents continuation of the running code until fixed.

It also makes const variables constant! They cannot be re-assigned later, else flag an error, unlike "let", partly.

// basic assignment behaviour:
let a = 1
a = 2
// no prob so far...!
// let a = 3
// Identifier 'a' has already been declared

const b = 2
// b = 3
// Uncaught TypeError: Assignment to constant variable.

var c = 1
c = 2
var c = 3
// no probs at all!! var is a wildcard that allows re-assignment at any point so makes keeping track of a var difficult across all scopes, whether global, local, functional or block scopes. It should not be used, but will exist in older code.

 

Hoisting was created to make certain programming requirements possible even if it causes some confusion, a by product of which was the neccesity for var also being hoisted (and now still allowed to be defined as "undefined" instead of uninitialised, if not declared - a bad idea)  in older versions of JS, which is why var should not be used any more, but let or const instead. The original JS design never intended or foresaw JS being used as it is now as such a powerful manipulator of CSS and HTML, due to Internet requirements.

So, how are variables hoisted or not if called before declaration?
// console.log(me); // hoisted ok but undefined yet
// undefined
// console.log(job); // in TDZ here
// Uncaught ReferenceError: Cannot access 'job' before initialization
// console.log(year); // in TDZ here
// Uncaught ReferenceError: Cannot access 'year' before initialization

// The let and const issues can be fixed by reversing their Top To Bottom order of being called

// var me = "Fred"
// let job = "teacher"
// const year = 1992

So how does function hoisting behave?

// Hoisting functions - declaration, expression and arrow:
console.log(addDecl(2,3));
// 5
// declaration hoisted ok

function addDecl(a,b) {
  return a + b 
}

// console.log(addExpr(2,3));
// Uncaught ReferenceError: Cannot access 'addExpr' before initialization

const addExpr = function (a,b) {
  return a +b 
}
As with the variables defined by const and let above before, const functions are in the TDZ until their Top To Bottom order has changed places 
console.log(addArrow(2,3));
// Uncaught ReferenceError: Cannot access 'addArrow' before initialization

const addArrow = (a,b) => a + b

As with the variables defined by const and let above before, const functions are in the TDZ until their Top To Bottom order has changed places
The situation with var is different for 2 reasons - first, the expression alone IS hoisted but undefined, as the variable examples above.
// console.log(addExpr);
// undefined

// but if arguments are given:
// console.log(addExpr(1,2));
// Uncaught TypeError: addExpr is not a function. This is because it is like using; 
// console.log(undefined(1,2));
// Uncaught TypeError: undefined is not a function

var addExpr = function (a, b) {
  return a + b;
};

// Obviously, the same applies to an Arrow func
console.log(addArrow(1,2));
// Uncaught ReferenceError: Cannot access 'addArrow' before initialization
let addArrow = (a, b) => a + b;

SUMMARY: ONLY function Declarations are hoisted by the JS Engine so work, no matter where - above or below each other - in the code the variable or declaration is placed.

// One last aspect of var is that it creates it's variables or functions as actual window objects, which may not be desireable:
var x  = 1
console.log(x  === window.x);
// true
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
// x: 1