Hoisting in javascript
Tags: javascript
Published: 2023-12-04 23:00:09

---
title: Hoisting in javascript
published: true
date: 2023-12-04 00:00:00 UTC
tags: Javascript
canonical_url: https://blog.melvinvmegen.com/posts/javascript/hoisting
---

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmxjh2o6vex13ezut3ww.png)

## What is a scope?

Let's start by answering the most important question, what is a **scope**? In JavaScript, a **scope** refers to the current execution context in which expressions are **defined** or can be **referenced**. I like to think of **scope** as layers where each layer has its own context that determines the accessibility of variables, functions and classes, let's see an example:

```js
const rootLayer = 0;
{
  const layer = 1;
  console.log(rootLayer) // 0
  console.log(layer) // 1
}

console.log(rootLayer) // 0
console.log(layer) // ❌ Uncaught ReferenceError: layer is not defined
```

## The global scope, the accessible one

When you start writing code at the root of any given file, you are in the global scope, there, if you define a variable, it will be accessible everywhere in your project. In the previous example the **rootLayer** variable is accessible everywhere including inside the block (represented by braces **{}**), contrary to the layer variable which is defined inside the block.

## The local scope, the hold

If you assign a variable inside a function (or any block for that matter) the declaration will happen in the local scope of that function so it is only accessible inside it.

```js
function scoped() {
  const scope = "local";
  console.log(scope) // local
}
console.log(scope) // ❌ Uncaught ReferenceError: scope is not defined 
```

The same goes for blocks, if you assign a variable inside a conditional statement for example, it will only be accessible inside the block.

```js
if (true) {
  const scope = "local";
  console.log(scope) // local
}
console.log(scope) // ❌ Uncaught ReferenceError: scope is not defined
```

**Note that:** with **var**, the scope of the variable is its current execution context which is either:
- In the case of a function, the enclosing function and the functions declared in it
- In the case of a block, the variables declared within it are accessible from the global scope.

*Otherwise just don't use **var** and you'll be fine.*

```js
function scoped() {
  var scope = "local" 
}
console.log(scope) // ❌ Uncaught ReferenceError: scope is not defined

if (true) {
  var scope = "local";
}
console.log(scope) // local
```

## The lexical scope, the nerdy?

The lexical scope is the definition area of an expression. Also called **static scope**, think of it as the place where it was defined, not to be confused with the place where the expression is invoked (or called).

```js
const dog = "Tom";
const parentFunction = () => {
  // Layer 1
  const cat = "Jerry"; // Lexical scope

  const childFunction = () => {
    // Layer 2
    console.log(cat);
    console.log(dog);
  }

  return childFunction();
}

parentFunction();
// "Jerry"
// "Tom"
```

As you can see, in the **childFunction**, we get to call *cat* and *dog* without errors but how, since they're both defined outside the block?

Well that's exactly what the lexical scoping does, inside the inner scope you can access variables of outer scopes. (It's called lexical (or static) because the engine determines (at [lexing time](https://en.wikipedia.org/wiki/Lexical_analysis)) the nesting of scopes just by parsing JavaScript source code, without executing it).

## The how, the closure

Okay, so now we know about the lexical scope allowing us to access variables from outer scopes, but how does it work exactly?

Let's get back to our previous example:

```js
const dog = "Tom";
const parentFunction = () => {
  // Layer 1
  const cat = "Jerry";

  const childFunction = () => {
    // Layer 2
    console.log(cat);
    console.log(dog);
  }

  return childFunction();
}

parentFunction();
// "Jerry"
// "Tom"
```

Inside **childFunction()** we get to call *cat* and *dog* but did you notice that **childFunction()** was called inside the **lexical scope** of **parentFunction()**?

Would it work if we instead did something like this:

```js 
const dog = "Tom";
const parentFunction = () => {
  // Layer 1
  const cat = "Jerry";

  const childFunction = () => {
    // Layer 2
    console.log(cat);
    console.log(dog);
  }

  return childFunction();
}

function run() {
  const myParentFunction = parentFunction();
  myParentFunction()
}

run();
// "Jerry"
// "Tom"
```
Now **childFunction()** is executed outside the **lexical scope** of **parentFunction()** but inside the **run()** . For this to work **childFunction** has to "remember" *cat* and *dog* from its lexical scope, the place where it was defined.

So there it is the final part, **closure** which is the ability of a function to "remember" and access its lexical scope regardless of where it's executed.

Thanks to **closure** the compiler does this:
1. Check if *cat* and *dog* are called in the local scope of **childFunction**, otherwise look for them up the chain (back to the parent function's scope).
2. Look for *cat* and *dog* inside the local scope of his parent, here **parentFunction**, the first one is found, perfect, we can print *Jerry*, but the other *dog* can't be found so we have to go back up the chain again.
3. Look for *dog* in the global scope, and here, *Tom* is finally printed.

This concept is called **scope chain**, going from one scope to another in order to find what needs to be stored for later use.

**Note that:** **hoisting** and **closure** allows functions, variables and classes to be used safely in the code (limited to their lexical scope) before being declared.

Note that it is generally not recommended to define everything explicitly at the top of the file, as this can lead to unexpected errors and memory as the engine cannot decide whether this variable should be garbage collected or not.

## Conclusion
Understanding the concept of scope and hoisting is crucial to writing efficient and effective code aswell as help you write better code and avoid common pitfalls when working with JavaScript.