DEV Community

Bryan Almaraz
Bryan Almaraz

Posted on • Updated on

Understanding The Execution Context & Stack

To become a good JavaScript developer you need to understand how JS is interpreted. Knowing how the execution context works will allow you to connect all the dots when it comes to learning about hoisting, scope, and closures. Let's get started.

This guide is meant to be a quick break down and easy reference when you need it.

  • What is the Execution Context
    • Execution Context Phases
    • Execution Context Types
  • What is the Execution Stack (Call Stack)
  • What is hoisting
  • What is scope
  • What are closures

What is the Execution Context?

Whenever JavaScript first runs your application code or invokes a function it creates an Execution Context. An Execution Context is a wrapper or container that helps manage the code currently executing in the program. It takes into account the environment (context) in which the code is currently executing.

When JavaScript first interprets your code it creates a Global Execution Context. This is like a global environment for your application. This global environment is a giant object. It then creates a special keyword for you called "this". It sets "this" as a pointer to the giant global object. If you are running your code in the browser the giant global object is called the window object. It also creates a reference to the outer environment.

In other words: The execution context is just a container that is currently running code and is taking into account the environment (context) in which it is running.

There are two phases to the Execution Context.

Execution Phases

The Creation phase and the Execution phase.

Creation Phase

  • Create a global object
  • Create a "this" keyword and assign it to the global object
  • Creates memory in its environment for the variables and functions inside the code
  • Sets variables to undefined & entire functions in memory
  • Sets a reference to its outer environment (null in Global EC)

Execution Phase

  • Starts executing your code line by line
  • Sets values to your variables.

Execution Types

The two important types of execution contexts in Javascript.

Global EC

Functional EC

The global EC we just discussed. The functional EC is not very different from the global EC. It also has the same two phases only with slight alterations in the creation phase.

  • Create a global object | Create an arguments object
  • Create a "this" keyword and assign it to the existing global object
  • Set aside memory in it's environment for the variables and functions inside the code
  • Sets variables to undefined & functions in memory
    • Except arguments passed in become variables and are assigned to memory with their value, not to undefined like regular variables.
  • Sets a reference to its outer environment

Execution Stack (Call Stack)

The execution stack or also known as the "call stack" is where execution contexts are stored. Keep in mind that a stack is a type of data structure that has one rule. "Last In, First Out".

When Javascript runs your script it creates the global execution contexts and pushes it on to the Execution Stack. This becomes the base of your execution stack and will stay there until your program is finally done before it pops off.

As mentioned before, the execution stack is just where execution contexts are stored. There are only two ways an execution context can be pushed onto the execution stack. The first is when your program starts up. The GEC is created and pushed onto the Execution Stack. The second is when JavaScript is executing your code and runs into a line that invokes a function. When a function is called a new execution context is created for that function. That Function Execution Context holds its "personal" variables and functions. Once that function is finished it pops off the Execution Stack and whatever was on the stack before that function was called is resumed.

Hoisting

When JavaScript starts the creation phase of the execution context it creates your variables in memory and sets them to undefined. It's really that simple.

So if you try using a variable before it was initialized it will be undefined (using the var keyword) instead of throwing an error. So it seems like your variable was "hoisted" to the top of the program because it knew that you had that variable. JavaScript just didn't know what the value was yet.

Scope

During the Execution Phase, JavaScript is creating new execution contexts for functions and already created the global execution context. So when referring to scope, it simply means what variables are in the current execution context. The variables and functions that are in that execution context are also in the scope.

When using a variable that is not in a function, but outside the FEC then the scope extends outwards (and only outwards) to find that variable.

Scope has a parent-child relationship where the child scope can ask the parent scope for values of variables but not vice-versa. This is called the scope chain.

Closures

When a function returns a function that references a variable in the outer function a closure is created.

A closure encloses or locks-in its environment variables (scope) and has references to them even after that execution context has been popped off the stack.

How this is possible?

When a function execution context is created remember that it creates it's own memory space for it's variables and functions. Once the function has finished and returns. The variables and functions that were created in that function are not deleted right away. They are just no longer accessible to the outer scope, but they are still somewhere in memory.

It's like the global scope lost its address and can no longer locate it. But when a function returns a function that uses a variable from the function that returned it. Then that function that was returned will always have a reference to those variables even if the rest of the program does not. It encloses its variables and will remember where to find them.

So a closure just makes sure its scope is always intact. A closure just knows where in memory its variables are, even if the execution context that created those variables is not on the call stack anymore.

Top comments (2)

Collapse
 
dingdingdong profile image
The D

Explained very well!! Thanks.

Collapse
 
thee_divide profile image
Bryan Almaraz

Thank you!