JavaScript Hoisting
Tags: javascript, codenewbie, beginners, webdev
Published: 2021-07-27 01:06:33


## [JS#5 WIL 🤔 Post]

JavaScript hoisting refers to the process where the compiler allocates memory for variable and function declarations **prior** to execution of code [[1](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)]. That means that declarations are moved to the top of their scope before code execution regardless of whether its scope is global or local. 

### Table Of Contents
* [Javascript Hoisting](#javascript-hoisting)
* [Javascript Context Execution: Creation and Execution Phase](#context-execution)
* [Default Values: `undefined` and `ReferenceError`](#default-values)
* [`let` hoisting and Temporal Dead Zone (TDZ)](#tdz)
* [Variable Hoisting](#hoisting-variables)
* [Function Hoisting](#function-hoisting)
* [Conclusion](#conclusion)

## :pushpin: Javascript Hoisting <a name="javascript-hoisting"></a>

Conceptually speaking, 
> hoisting is the compiler splitting variable declaration and initialization and moving only the declarations to the top of the code.

Thus, variables can appear in code before they are even defined. However, the variable initialization will only happen until that line of code is executed.

The code snippets below show hoisting in action. The first snippet shows how the code is expected to be written: **declare the function first and use/invoke it after.**

```javascript
function printIamHoisted(str) {
   console.log(str);
}

printIamHoisted("Am I hoisted??")
```

On the snippet below, the method is invoked first, and the function declaration is written next. However, both of them have the same output - they print the string `Am I hoisted?`

```javascript
printIamHoisted("Am I hoisted?")

function printIamHoisted(str) {
   console.log(str);
}
```

So even though the function is called before it is written, the code still works. This happens because of how context execution works in Javascript.

### :pushpin: Javascript Context Execution <a name="context-execution"></a>
When a Javascript engine executes code, it creates execution context. Each context has two phases: **creation** and **execution**.

#### :pushpin: Creation Phase
When a script executes, the JS engine creates a **Global Execution Context**. In this phase, it performs the following tasks:
* Create a global object `window` in the web browser
* Create a `this` object binding which pertains to the global object
* Setup memory for storing variables and function references
* Store the declarations in memory within the global execution context with `undefined` initial value.

Lookin back at this snippet, 
```javascript
printIamHoisted("I am hoisted!!!")

function printIamHoisted(str) {
   console.log(str);
}
```
the global execution context at this phase, would somehow look like this ![Creation Phase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4t3uitokcxtbs7qil5x8.png).

#### :pushpin: Execution Phase
At this phase, the JS engine executes the code line by line. But by virtue of hoisting, the function is declared regardless of line order, so there is no problem calling/invoking the method prior the declaration.

For every function call, the JS engine creates a new **Function Execution Context**. This context is similar to **global execution context**, but instead of creating the global object, it creates the `arguments` object that contains references to all the parameters passed to the function. The context during this phase would look somewhat like : 
![Execution Phase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/px04iqhq9b2qtr72i4sl.png)

### :pushpin: Only the declarations (function and variable) are hoisted 
JS only hoists declarations, **not initializations**. If a variable is used but it is only declared and initialized after, the value when it is used will be the default value on initialization. 

#### :pushpin: Default Values: `undefined` and `ReferenceError` <a name="default-values"></a>
For variables declared with the `var` keyword, the default value would be `undefined`. 
```javascript
console.log(hoistedVar); // Returns 'undefined' from hoisted var declaration (not 6)
var hoistedVar; // Declaration
hoistedVar = 78; // Initialization
```
Logging the `hoistedVar` variable before it is initialized would print `undefined`. If however, the declaration of the variable is removed, i.e.
```javascript
console.log(hoistedVar); // Throw ReferenceError Exception
hoistedVar = 78; // Initialization
```
a `ReferenceError` exception would be thrown because no hoisting happened.

#### :pushpin: `let` hoisting: Temporal Dead Zone (TDZ)<a name="tdz"></a>
Variables declared with `let` and `const` are also hoisted. However, unlike variables declared with `var`, they are not initialized to a default value of `undefined`. Until the line in which they are initialized is executed, any code that access them, will throw an exception. These variables are said to be in a "temporal dead zone" (TDZ) from the start of the block until the initialization has completed. Accessing unintialized `let` variables would result to a `ReferenceError`.

```javascript
{ // TDZ starts at beginning of scope
  console.log(varVariable); // undefined
  console.log(letVariable); // ReferenceError
  var varVariable = 1;
  let letVariable = 2; // End of TDZ (for letVariable)
}
```

The term "temporal" is used because the zone depends on the execution order (referring to time - _temporal_) rather than the order in which the code is written (position). However, the code snippet below will work because even though `sampleFunc` uses the `letVariable` before it is declared, the function is **called** outside of the TDZ.

```javascript
{
    // TDZ starts at beginning of scope
    const sampleFunc = () => console.log(letVariable); // OK

    // Within the TDZ letVariable access throws `ReferenceError`

    let letVariable = 97; // End of TDZ (for letVariable)
    sampleFunc(); // Called outside TDZ!
}
```

### :pushpin: Variable Hoisting <a name="hoisting-variables"></a>
Remember that all function and variable declarations are hoisted to the **TOP** of the scope. Declarations are processed **before** any code is executed. With this, undeclared variables do not exist until the code assignment is executed. Variable assignment to an undeclared variable implicitly creates it as a **global** variable when the assignment is executed. That means that any undeclared variable (but assigned) is a global variable.

```javascript
function demo() {
   globalVar = 34;
   var functionScopedVar = 78;
}

demo();
console.log(globalVar); // Output: 34
console.log(functionScopedVar) // throws a ReferenceError
```
_This is why it is always good to declare variables regardless of whether they are of function or global scope._

#### :pushpin: ES5 Strict Mode
Introduced in EcmaScript 5, strict mode is a way to opt in to a restricted variant of JS. Strict mode make several changes to normal JS semantics
1. Eliminates silent errors by throwing them
2. Prohibits syntax that might be defined in future version of ES
3. Fix mistakes that make JS engines perform optimizations

With regards to hoisting, using strict mode will not tolerate the use of variables before they are declared. 

### :pushpin: Function Hoisting <a name="function-hoisting"></a>
JS functions can be declarations or expressions. 

Function declarations are hoisted completely to the top. That is why a function can be invoked even before it is declared.

```javascript
amIHoisted(); // Output: "Yes I am."
function amIHoisted() {
   console.log("Yes I am.")
}
```

Function expressions, are **NOT** hoisted. This is because of the precedence order of JS functions and variables. The snippet below would throw a `TypeError` because the hoisted variable `amIHoisted` is treated as a variable, not a function.
 
```javascript
amIHoisted(); //Output: "TypeError: expression is not a function
var amIHoisted = function() {
   console.log("No I am not.");
}
```

The code execution of the code above would somehow look like this 
```javascript
var amIHoisted; // undefined
amIHoisted(); 
/*Function is invoked, but from the interpreter's perspective it is not a function. 
Thus would throw a type error
*/
amIHoisted = function() {
   console.log("No I am not.");
}
/*The variable is assigned as a function late. It was already invoked before the assignment.*/
```
#### :pushpin: Hoisting Order of Precedence
* Variable assignment takes precedence over function declaration. 
The type of `amIABoolean` would be a `boolean` because the variable is assigned to a value `true`.
```javascript
var amIABoolean = true;

function amIABoolean() {
  console.log("No.")
}

console.log(typeof amIABoolean); // Output: boolean
```

* Function declarations take precedent over variable declarations. From the snippet below, the type of `amIAFunction` would be a `function` because on the first line, the variable is only declared, not assigned. Since function declarations takes precedence, it is resolved to type `function`.

```javascript
var amIAFunction;

function amIAFunction() {
  console.log("Yes.")
}

console.log(typeof amIAFunction); // Output: function
```
 
### Conclusion <a name="conclusion"></a>
Hoisting in JS  is the compiler splitting variable declaration and initialization and moving only the declarations to the top of the code. So even though the functions and variables are called/used before they are written, the code still works. This happens because of how context execution works in Javascript.

Note that only declarations are hoisted, **not initializations**. For variables declared with the `var` keyword, the default value would be `undefined`. For `let` variables, until the line in which they are initialized is executed, any code that access them, will throw an exception. These variables are said to be in a "temporal dead zone" (TDZ) from the start of the block until the initialization has completed. Accessing unintialized `let` variables would result to a `ReferenceError`.

That is it for the basics of JS hoisting, my fifth WIL(_What I Learned_) dev post :smile:.

As always, cheers to lifelong learning :wine_glass:! 

### [REFERENCES]
1. [MDN Web Docs : Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
2. [Understanding Hoisting in Javascript](https://www.digitalocean.com/community/tutorials/understanding-hoisting-in-javascript)
3. [Javascript Context Execution](https://www.javascripttutorial.net/javascript-execution-context/)
4. [Temporal Dead Zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz)
5. [Strict Mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)