Execution Contexts and Hoisting in JavaScript

The JavaScript interpreter uses the concept of execution contexts. Every statement in a script lives in one of three execution contexts:

Global Context

Code that is in the script, but not in a function. There is only one global context in any script.

Function Context

Code that is being run within a function. Each function has its own function context.

Eval Context

Code executed inside an eval function also gets its own execution context, but as eval isn’t usually used, so I will not discuss it here.

Each execution context also creates its own variable object. This object contains details of all of the variables, functions, and parameters for that execution context.

Now, each time a script enters a new execution context, there are two phases of activity:

Prepare Phase

During the prepare phase, JavaScript sets up the environment for the code that is about to be executed. This involves:

  • New Scope Creation: An execution context is associated with a scope. When a new execution context is created, a new scope is also established.

  • Variable and Function Creation: JavaScript scans through the code in the current scope and identifies all variables and function declarations. Variables, functions, and arguments are created. Variables are created with undefined value.

  • The value of the this keyword is determined.

Execute Phase

Once the prepare phase is complete and the environment is set up, JavaScript proceeds to execute the actual statements and expressions in the code.

  • Variable Assignment: Previously declared variables are assigned values. This happens as the program encounters assignments in the code.

  • Function Execution: If a function is called within the code, its code block is executed.

  • Statement Execution: Regular code statements are executed in sequence as they are encountered.

Now that you have an understanding of how these two phases operate, we can dive into hoisting.

You might have noticed that you can:

  • Call functions before they are officially declared (if they were made using function declarations)

  • Assign a value to a variable that hasn't been declared yet

This is possible because all the variables and functions within each execution context are set up before they're actually executed.

It's like lifting and placing all the variables and functions at the top of the execution context, getting them ready for use. This is referred to as hoisting.

There are two things in JavaScript that are hoisted:

  • Function declarations

  • Variable declarations

Hoisting Function Declarations

We will focus on function declarations first.

When you run your JavaScript file, the JavaScript compiler will take all of your function declarations and move them to the top of the file so they are all available before you use them.

Because of hoisting, you can technically run a function before it exists.

Let's do an example.

Add the following function

function add(a, b) {
  return a + b;
}

If you try to run this function before it exists, will it work?

add(5, 10);
function add(a, b) {
  return a + b;
}

It does.

That is because hoisting moves them to the top before it will actually run anything.

Only function declaration types of functions are hoisted, not function expressions (when you put a function in a variable).

The same thing goes with arrow function or any other type of function.

Variable Hoisting

The other type of hoisting is called variable hoisting.

If you were to go to the top of the file and add

console.log(age);
var age = 10;

What will happen? Is it going to error? Undefined? 10?

The value is undefined.

Why is that?

What is happening is that JavaScript will hoist the variable declarations but it will not set its values.

It is basically doing the equivalent of the following

var age;
console.log(age);
age = 10;

It is important to note that although let and const variables are hoisted, they are not initialised to undefined, unlike var variables. Hence, in the above example, if you replace var with let or const, you would be presented with a ReferenceError saying age is not defined

Get my free, weekly JavaScript tutorials

Want to improve your JavaScript fluency?

Every week, I send a new full-length JavaScript article to thousands of developers. Learn about asynchronous programming, closures, and best practices — as well as general tips for software engineers.

Join today, and level up your JavaScript every Sunday!

Thank you, Taha, for your amazing newsletter. I’m really benefiting from the valuable insights and tips you share.

- Remi Egwuda