Hoisting
Published: 2020-07-31 16:28:18

One of the trickier aspects of JavaScript for new JavaScript developers is the fact that variables and functions are "hoisted." Rather than being available after their declaration, they might actually be available beforehand. How does that work? Let's take a look at variable hoisting first.

```js
// ReferenceError: noSuchVariable is not defined
console.log(noSuchVariable);
```
### ❗The JavaScript Interpreter
When you execute your JavaScript code, the interpreter goes through the code twice.

- The first run through the code is where it does a safety check and small optimizations of your code. Safety checks such as making sure that the syntax is right, if there are any calls to eval or with, etc. Then, it optimizes the code as best as it can to ensure better performance when it is executed. This is also where hoisting occurs (more on this soon). This is also referred to as the compile run.

- The second run is where it actually executes your code by going through it line by line, doing the assignments, calling the functions, and so on.

###❗ What is Hoisting?

🛑  Hoisting is when the JavaScript interpreter moves all variable and function declarations to the top of the current scope. It's important to keep in mind that only the actual declarations are hoisted, and that assignments are left where they are.

👉 Hoisting is done during the interpreter's first run through the code.

### Variable Declarations

Let's start with a basic example and look at the following code:

```js
'use strict';

console.log(book);
var bar = 'book';
console.log(book);

// output: undefined , book
```
At a first glance, you may think that the code would throw a `ReferenceError` on (console.log(book);) because `book` has not been `declared` yet. 
👉 However, with the magic of `hoisting`, it won't throw a `ReferenceError` but the `value` of `book` will be `undefined` at that point. This is because the JavaScript <b>interpreter</b> does a first run through the whole code and declares all variables and functions at the top of the current scope, and then, on the second run, will execute the code.

- Here's what the same code would look like after the interpreter's first run:

```js
'use strict';
var book;
console.log(book); // undefined
bar = 'book';
console.log(book); // 'book'
```
Notice how `book` is now declared at the top `(var book)` but is not yet assigned at that point? It's a subtle but important difference, and this is why `book` is logged as undefined instead of throwing a `ReferenceError`.

###❗ Function Declarations

👉 Hoisting also applies to <b>function declarations</b> (not function expressions). Let's analyse the following sample code:
```js
'use strict';

read();
function read() {
    console.log(book);
    var book = 'book';
}

console.log(book);
```
In this sample code, we are able to successfully call the function `read` since it's a <b>function declaration </b> and therefore it is` hoisted` as-is to the top of the `current scope`. 
👉 Then, `read` will output undefined when calling it since, as in the previous example, book is hoisted to the top of its `current scope`, which is function `read()`. This means that `book` was declared before calling `console.log(book)` but it has not yet been assigned a value `(book = 'book')`.

👉 🛑 The important thing to note here is that `book` was hoisted at the top of its `current scope`. 
- This means that it was not declared in the <b>global scope</b> but in the <b>function's scope</b> instead.

```js
'use strict';

function read() {
    var book;
    console.log(book); // undefined
    book = 'book';
}

read();
console.log(book); // ReferenceError: book is not defined

```


###🛑 ❗ Pay attention ❗❗

-  How `read()` was moved to the top, and `book` is declared in `read()`? This means that, when you call `console.log(book)`, it will not find the variable book in the `general scope` and will throw a `ReferenceError`.


### ❗ Function Expressions

the third use case I'd like to cover is how function expressions are not hoisted as opposed to function declarations. Instead, it's their <b> variable declarations that are hoisted.</b>

```js
'use strict';

book();
var book = function () {
    console.log(read);
    var read = 'read';
}

// output: Uncaught TypeError: book is not a function
```
This code throws a TypeError: book is not a function error since only the <b> variable declaration</b> ` var book` is hoisted to the top of the file, and the assignment of the function to book is done on the interpreter's second run only.

- Here's what the same code would look like after the interpreter's first run:
```js
'use strict';

var book;
book(); // `book` has not been assigned the function yet
book = function () {
    console.log(read);
    var read = 'read';
}
```
### What Takes Precedence?

the last use case I'd like to cover is that function declarations are hoisted before variables. Let's have a look at the following code:
```js

'use strict';

console.log(typeof book);

var book = 'book';

function book () {
    var read = 'read';
    console.log(read);
}
//output: function
```
In this example, `typeof book` returns `function` instead of `string`, even though the function `book()` is declared after the `variable`. This is because <b>function declarations are hoisted before variable declarations,</b> so `book = 'book'` is executed on the second run, after calling `typeof book`.

On the first run, the interpreter will hoist `book()` at the top of the <b>current scope,</b> and then will get to the var book = 'book' line. At that point, it realises that `book` was already declared so it doesn't need to do anything and will continue its first run through the code.

Then, on the second run (which basically executes the code), it'll call `typeof book` before it gets to the assignment
 ` book = 'book' `.

Here's what the same code would look like after the interpreter's first run:
```js
'use strict';

function book () {
    var read = 'read';
    console.log(read);
}

console.log(typeof book); // 'function'
book = 'book';
```

👉 We use ` var` to declare a variable, and we have initialized it with a value. After declaring and initializing, we can access or reassign the variable.

👉 If we attempt to use a `variable` before it has been declared and initialized, it will return `undefined`.

```js
// Attempt to use a variable before declaring it
console.log(x);
```
```js
// Variable assignment
var x = 100;
//Output: undefined
```
👉 However, if we omit the var keyword, we are no longer declaring the variable, only<b> initializing</b> it. It will return a ReferenceError and halt the execution of the script.

```js
// Attempt to use a variable before declaring it
console.log(x);

// Variable assignment without var
x = 100;
//Output: ReferenceError: x is not defined
```
🛑 ❗ The reason for this is due to hoisting, a behavior of JavaScript in which variable and function declarations are moved to the<b> top of their scope</b>. Since only the actual declaration is hoisted, not the initialization, the `value` in the first example returns `undefined`.

To demonstrate this concept more clearly, below is the code we wrote and how JavaScript actually interpreted it.

```js
// The code we wrote
console.log(x);
var x = 100;

// How JavaScript interpreted it
var x;
console.log(x);
x = 100;
```

👉 JavaScript saved `x` to memory as a variable before the execution of the script. Since it was still called before it was defined, the result is undefined and not 100. However, it does not cause a ReferenceError and halt the script. Although the var keyword did not actually change location of the var, this is a helpful representation of how hoisting works. This behavior can cause issues, though, because the programmer who wrote this code likely expects the output of `x `to be `true`, when it is instead `undefined`.

We can also see how <b>hoisting </b> can lead to unpredictable results in the next example:

```js
// Initialize x in the global scope
var x = 100;

function hoist() {
  // A condition that should not affect the outcome of the code
  if (false) {
    var x = 200;
  }
  console.log(x);
}

hoist();
// Output: undefined
```
👉  In this example, we declared `x` to be `100` globally. Depending on an `if statement`, `x` could change to `200`, but since the condition was false it should not have affected the value of `x`. Instead, `x` was hoisted to the top of the `hoist()` function, and the `value` became undefined.

👉 This type of unpredictable behavior can potentially cause `bugs` in a program. Since `let` and `const` are <b> block-scoped</b>, they will not<b> hoist</b> in this manner, as seen below.

```js
// Initialize x in the global scope
let x = true;

function hoist() {
  // Initialize x in the function scope
  if (3 === 4) {
    let x = false;
  }
  console.log(x);
}

hoist();
// Output:  true
```
👉 <b>Duplicate declaration of variables</b>, which is possible with `var`, will throw an error with `let` and `const`.

```js
// Attempt to overwrite a variable declared with var
var x = 1;
var x = 2;

console.log(x);
//Output: 2
```
```js
// Attempt to overwrite a variable declared with let
let y = 1;
let y = 2;

console.log(y);
// Output: Uncaught SyntaxError: Identifier 'y' has already been declared

```

👉 To summarise, variables introduced with `var` <b> have the potential of being affected by hoisting</b>, a mechanism in JavaScript in which<b> variable declarations are saved to memory.</b> This may result in <b>undefined variables </b> in one’s code.
👉 ❗ The introduction of `let` and `const` resolves this issue by<b> throwing an `error` when attempting to use a variable before declaring it or attempting to declare a variable more than once.</b>