JavaScript Hoisting: A Memo


I admit, as a (mainly) backend developer, JavaScript is not a language I explored extensively at work.

But it's good to go back to basic sometimes. So this time I'd like to talk about variable and function hoisting. I'm sure there are better readings out there but I want to make my own memo.

# Variable hoisting

JavaScript treats all variables declared anywhere using `var` as if they are declared on top of the functional scope. To put it simply, it does not matter where the location is, that `var` will have a value of [`undefined`](https://developer.mozilla.org/en-US/docs/Glossary/undefined) even before its actual declaration.

This is called `hoisting`, as in hoisting a flag to the top of the pole it's attached to.

```javascript
console.log(color); // Expected output: undefined
var color = 'gray';
console.log(color); // Expected output: gray
```

Another example with function. You can see much clearly here that you will get the color even if you don't pass a parameter to the function.

```javascript
getColor() // Expected output: undefined
getColor('blue') // Expected output: gray

function getColor(color) {
    if (color) {
        var color = 'gray';
        console.log(color);
    } else {
        console.log(color);
    }
}
```

## What about let and const?

Well, declaring `let` is similar to declaring a variable in C-based languages -- you need to declare it first before accessing its value.

```javascript
console.log(color); // This will throw error immediately
let color = 'green';
```

```bash
console.log(color); // This will throw error immediately
            ^
ReferenceError: Cannot access 'color' before initialization
```

That's because `let`, as opposed to `var`, has a scope limited to code block only and not subject to `hoisting`.

Similarly, `const` will throw an error if accessed before the declaration.

```javascript
console.log(color); // This will throw error immediately
const color = 'yellow';
```

```bash
console.log(color); // This will throw error immediately
            ^
ReferenceError: Cannot access 'color' before initialization
```

## The takeaway

Using `var` without realizing its characteristics will lead to unexpected results.

I personally prefer to use `const` whenever possible, or if I need to reassign variables, using `let` to limit the scope would be a good choice. 

# Function hoisting

Not only `var` variables can be subjected to `hoisting`. Functions can too! But it's only when a function is defined through declaration, not expression.

## Declaration

You may notice in my above example I called function `getColor` even before it's declared and it doesn't throw an error. That's because a declared function is hoisted by JavaScript interpreter.

```javascript
callBeforeDeclaration();

function callBeforeDeclaration() {
    console.log("This is fine"); // Will be printed
}
```

## Expression

In contrast, any function defined through expression will not be hoisted. Function expression has a similar syntax to function declaration but with one difference: it can be anonymous (not having a function name).

Using an anonymous function before its expression will yield a type error.

```javascript
console.log(callBeforeExpression); // This is subject to hoisting
callBeforeExpression(); 

var callBeforeExpression = function() {
   console.log("This is not fine");
};
```

```bash
callBeforeExpression();
^
TypeError: callBeforeExpression is not a function
```

> When using function expression, it's advisable to use `const` instead of `var`.

# More examples of hoisting

#### Exhibit 1

```javascript
var color = "green";
function getColor() {
    color = "red"; // This will have no effect
    function color() {} // Gets hoisted
}
getColor();
console.log(color);
```

What's the result? It's `green`. 🤣
Because `function color()` got hoisted, assignment color to red inside `function getColor()` is reduced to the local scope and therefore does not have any effect on the global variable `var color`.

```javascript
// What really happened
var color = "green";
function getColor() {
    function color() {} // Hoisted
    color = "red"; // Reduced to local scope
}
getColor();
console.log(color);
```

#### Exhibit 2

```javascript
var color = "red";
console.log(color);
var color = "blue";
console.log(color);
```

This gives us red and blue respectively. Why? 🤔
Behind the scene, JavaScript hoisted color to the top.

```javascript
// What really happened
var color = undefined
color = "red"
color = "blue"
```

#### Exhibit 3

```javascript
getColor()
function getColor() {
    console.log("white");
}
getColor();
function getColor() {
    console.log("black");
}
```

What's the result of console logging? Both are `black`. 🤣
Setting aside the bizarre fact that we could define 2 functions with the exact names with no error in JavaScript, these functions are subjected to hoisting.

```javascript
// What really happened
function getColor() {
    console.log("white");
}
function getColor() { // Declared last, became the de facto function
    console.log("black"); 
}
getColor();
getColor();
```
That's it for now. I will add more examples if I got time. Stay tuned!