Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
14K views

JavaScript Execution Context - How JS Works Behind The Scenes

This document discusses how JavaScript code is executed behind the scenes in a web browser. It explains the key concepts of the JavaScript engine, parser, and execution contexts including the global execution context and function execution context. The creation phases and processes that occur are also described.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14K views

JavaScript Execution Context - How JS Works Behind The Scenes

This document discusses how JavaScript code is executed behind the scenes in a web browser. It explains the key concepts of the JavaScript engine, parser, and execution contexts including the global execution context and function execution context. The creation phases and processes that occur are also described.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 18

JavaScript Execution Context – How JS

Works Behind The Scenes


Victor Ikechukwu
February 10, 2022

All JavaScript code needs to be hosted and run in some kind of


environment. In most cases, that environment would be a web browser.

For any piece of JavaScript code to be executed in a web browser, a lot of


processes take place behind the scenes. In this article, we'll take a look at
everything that happens behind the scenes for JavaScript code to run in a
web browser.

Before we dive in, here are some prerequisites to familiarize yourself with,
because we'll use them often in this article.

Parser: A Parser or Syntax Parser is a program that reads your code


line- by-line. It understands how the code fits the syntax defined by
the Programming Language and what it (the code) is expected to do.

JavaScript Engine: A JavaScript engine is simply a computer


program that receives JavaScript source code and compiles it to the
binary instructions (machine code) that a CPU can understand.
JavaScript engines are typically developed by web browser vendors,
and each major browser has one. Examples include the V8 engine for
Google chrome, SpiderMonkey for Firefox, and Chakra for Internet
Explorer.

Function Declarations: These are functions that are assigned a name.

functiondoSomething()
Function Expressions: These are anonymous functions, that is
{//here"doSomething"isthefunction'sname
functions without a function name like js function () { statements } .
statements;
They are usually used in statements, like assigning a function to a
}
variable. let someValue = function () { statements } .

Now, that we've gotten those out of the way, let's dive in.

How JavaScript Code Gets Executed


For does who don't know, the browser doesn't natively understand the
high- level JavaScript code that we write in our applications. It needs to
be
converted into a format that the browser and our computers can understand
– machine code.

While reading through HTML, if the browser encounters JavaScript code to


run via a <script> tag or an attribute that contains JavaScript code like
onClick , it sends it to its JavaScript engine.

The browser's JavaScript engine then creates a special environment to


handle the transformation and execution of this JavaScript code. This
environment is known as the Execution Context .

The Execution Context contains the code that's currently running, and
everything that aids in its execution.

During the Execution Context run-time, the specific code gets parsed by a
parser, the variables and functions are stored in memory, executable
byte- code gets generated, and the code gets executed.

There are two kinds of Execution Context in JavaScript:

Global Execution Context (GEC)

Function Execution Context (FEC)

Let's take a detailed look at both.

Global Execution Context (GEC)


Whenever the JavaScript engine receives a script file, it first creates a default
Execution Context known as the Global Execution Context (GEC) .

The GEC is the base/default Execution Context where all JavaScript code
that is not inside of a function gets executed.

For every JavaScript file, there can only be one GEC.

Function Execution Context (FEC)


Whenever a function is called, the JavaScript engine creates a different
type of Execution Context known as a Function Execution Context (FEC)
within the GEC to evaluate and execute the code within that function.

Since every function call gets its own FEC, there can be more than one
FEC in the run-time of a script.

How are Execution Contexts Created?


Now that we are aware of what Execution Contexts are, and the different
types available, let's look at how the are created.
The creation of an Execution Context (GEC or FEC) happens in two phases:

1. Creation Phase

2. Execution Phase

Creation Phase
In the creation phase, the Execution Context is first associated with an
Execution Context Object (ECO). The Execution Context Object stores a
lot of important data which the code in the Execution Context uses
during its run-time.

The creation phase occurs in 3 stages, during which the properties of the
Execution Context Object are defined and set. These stages are:

1. Creation of the Variable Object (VO)

2. Creation of the Scope Chain

3. Setting the value of the this keyword

Let us go over each phase in detail.

Creation Phase: Creation Of The Variable Object (VO)


The Variable Object (VO) is an object-like container created within an
Execution Context. It stores the variables and function declarations
defined within that Execution Context.

In the GEC, for each variable declared with the var keyword, a property is
added to VO that points to that variable and is set to 'undefined'.

Also, for every function declaration, a property is added to the VO,


pointing to that function, and that property is stored in memory. This
means that all the function declarations will be stored and made accessible
inside the VO, even before the code starts running.

The FEC, on the other hand, does not construct a VO. Rather, it
generates an array-like object called the 'argument' object, which
includes all of the arguments supplied to the function. Learn more about
the argument object here.

This process of storing variables and function declaration in memory prior


to the execution of the code is known as Hoisting. Since this is an
important concept, we'll talk about it briehy before moving on to the next
stage.

Hoisting in JavaScript
Function and variable declarations are hoisted in JavaScript. This means
that they are stored in memory of the current Execution Context's VO and
made
available within the Execution Context even before the execution of the
code begins.

Function Hoisting
In most scenarios when building an application, developers can choose to
define functions at the top of a script, and only later call them down the
code, like so:

However, due to hoisting, the opposite will still work. Where we can call
functions first then define them later down the script.

In the code above, the getAge function declaration will be stored in the
memory of the VO, making it available for use even before it is defined.

Variable Hoisting
Variables initialized with the var keyword are stored in the memory of the
current Execution Context's VO as a property, and initialized with the
value undefined . This means, unlike functions, trying to access the value of
the variable before it is defined will result in undefined .

Ground Rules of Hoisting


Hoisting only works for function declarations, not expressions. Here is an
example of a function expression where the code execution will break.

getAge(1990);
The code execution breaks, because with function expressions, getAge will
bevargetAge=function(yearOfBirth)
hoisted as a variable not as a function. And with variable hoisting, its
{console.log(newDate().getFullYear-yearOfBirth)
value
};
will be set to undefined . That's why we get the error:

Also, variable hoisting does not work for variables initialized with the let or
const keyword. Trying to access a variable ahead of declaration and use

the let and const keywords to declare it later will result in a


ReferenceError .

In this case, they will be hoisted but not assigned with the default value of
undefined . js console.log(name); let name = "Victor"; will throw the error:

Creation Phase: Creation of The Scope Chain


After the creation of the Variable Object (VO) comes the creation of the
Scope Chain as the next stage in the creation phase of an Execution
Context.

Scope in JavaScript is a mechanism that determines how accessible a


piece of code is to other parts of the codebase. Scope answers the
questions: from where can a piece of code be accessed? From where can't
it be accessed?
What can access it, and what can't?

Each Function Execution Context creates its scope: the


space/environment where the variables and functions it defined can be
accessed via a process called Scoping.

This means the position of something within a codebase, that is, where a
piece of code is located.

When a function is defined in another function, the inner function has


access to the code defined in that of the outer function, and that of its
parents. This behavior is called lexical scoping.
However, the outer function does not have access to the code within the
inner function.

This concept of scope brings up an associate phenomenon in JavaScript


called closures. These are when inner functions that always get access to
the code associated with the outer functions, even after the execution of
the outer functions is complete. You can learn more closures here.

Let's look at some examples to get a better understanding:

On the right is the Global Scope. It is the default scope created when a
.js script is loaded and is accessible from all functions throughout the

code.

The red box is the scope of the first function, which defines the
variable b = 'Hello!' and the second function.

In green is the scope of the second function. There is a console.log


statement which is to print the variables a , b and c .

Now the variables a and b aren't defined in the second function, only c .
However, due to lexical scoping, it has access to the scope of the
function it sits in and that of its parent.
In running the code, the JS engine will not find the variable b in the scope
of the second function. So, it looks up into the scope of its parents,
starting with the first function. There it finds the variable b = 'Hello' . It
goes back to the second function and resolves the b variable there with it.

Same process for the a variable. The JS engine looks up through the scope
of all its parents all the way to the scope of the GEC, resolving its value in
the second function.

This idea of the JavaScript engine traversing up the scopes of the


execution contexts that a function is defined in in order to resolve
variables and functions invoked in them is called the scope chain.

Only when the JS engine can't resolve a variable within the scope chain
does it stop executing and throws an error.

However, this doesn't work backward. That is, the global scope will never
have access to the inner function’s variables unless they are returned from
the function.

The scope chain works as a one-way glass. You can see the outside, but
people from the outside cannot see you.

And that is why the red arrow in the image above is pointing upwards
because that is the only direction the scope chains goes.

Creation Phase: Setting The Value of The "this"


Keyword
The next and final stage after scoping in the creation phase of an
Execution Context is setting the value of the this keyword.

The JavaScript this keyword refers to the scope where an Execution


Context belongs.

Once the scope chain is created, the value of 'this' is initialized by the JS
engine.
"this" in The Global Context
In the GEC (outside of any function and object), this refers to the global
object — which is the window object.

Thus, function declarations and variables initialized with the var keyword
get assigned as properties and methods to the global object – window
object.

This means that declaring variables and functions outside of any function,
like this:

Is varoccupation="FrontendDeveloper";
exactly the same as:

functionaddOne(x)
window.occupation="FrontendDeveloper";w
Functions and variables in the GEC get attached as methods and
{console.log(x+1)
indow.addOne=(x)=>{
properties to the window object. That's why the snippet below will return
}console.log(x+1)
true.
};

"this" in Functions
In the case of the FEC, it doesn't create the this object. Rather, it get's access to
that of the environment it is defined in.

Here that'll be the window object, as the function is defined in the GEC:
varmsg="Iwillruletheworld!";

functionprintMsg()
{console.log(this.msg)
;
}

In objects, the this keyword doesn't point to the GEC, but to the object
itself. Referencing this within an object will be the same as:

theObject.thePropertyOrMethodDefinedInIt;

Consider the code example below:

varmsg="Iwillruletheworld!";cons
The code logs "Victor will rule the world!" to the console, and not "I will
tVictor={
rule the world!" because in this case, the value of the this keyword the
msg:"Victorwillruletheworld!",prin
function has access to is that of the object it is defined in, not the global
tMsg(){console.log(this.msg)},
object.
};

With the value of the this keyword set, all the properties of the Execution
Victor.printMsg();//
Context Object have been defined. Leading to the end of the creation
logs"Victorwillruletheworld!"totheconsole.
phase, now the JS engine moves on to the execution phase.

The Execution Phase


Finally, right after the creation phase of an Execution Context comes the
execution phase. This is the stage where the actual code execution
begins.

Up until this point, the VO contained variables with the values of undefined
. If the code is run at this point it is bound to return errors, as we can't
work with undefined values.

At this stage, the JavaScript engine reads the code in the current
Execution Context once more, then updates the VO with the actual values
of these
variables. Then the code is parsed by a parser, gets transpired to
executable byte code, and finally gets executed.

JavaScript Execution Stack


The Execution Stack, also known as the Call Stack, keeps track of all the
Execution Contexts created during the life cycle of a script.

JavaScript is a single-threaded language, which means that it is capable of


only executing a single task at a time. Thus, when other actions,
functions, and events occur, an Execution Context is created for each of
these events. Due to the single-threaded nature of JavaScript, a stack of
piled-up execution contexts to be executed is created, known as the
Execution Stack .

When scripts load in the browser, the Global context is created as the
default context where the JS engine starts executing code and is placed at
the bottom of the execution stack.

The JS engine then searches for function calls in the code. For each
function call, a new FEC is created for that function and is placed on top
of the currently executing Execution Context.

The Execution Context at the top of the Execution stack becomes the
active Execution Context, and will always get executed first by the JS
engine.

As soon as the execution of all the code within the active Execution
Context is done, the JS engine pops out that particular function's
Execution Context of the execution stack, moves towards the next below
it, and so on.

To understand the working process of the execution stack, consider the


code example below:

var name = "Victor";

function first() { var a = "Hi!";


second();
console.log(`${a} ${name}`);
}

function second() { var b = "Hey!";


third();
console.log(`${b} ${name}`);
}
function third() { var c = "Hello!";
console.log(`${c} ${name}`);
}

first();

First, the script is loaded into the JS engine.

After it, the JS engine creates the GEC and places it at the base of the
execution stack.

The name variable is defined outside of any function, so it is in the GEC


and stored in it's VO.

The same process occurs for the first , second , and third functions.

Don't get confused as to why they functions are still in the GEC.
Remember, the GEC is only for JavaScript code (variables and functions)
that are not inside of any function. Because they were not defined
within any function, the function declarations are in the GEC. Make sense
now 😃?

When the JS engine encounters the first function call, a new FEC is created
for it. This new context is placed on top of the current context, forming
the so- called Execution Stack .
For the duration of the first function call, its Execution Context becomes
the active context where JavaScript code is first executed.

In the first function the variable a = 'Hi!' gets stored in its FEC, not in the
GEC.

Next, the second function is called within the first function.

The execution of the first function will be paused due to the single-
threaded nature of JavaScript. It has to wait until its execution, that is the
second function, is complete.

Again the JS engine sets up a new FEC for the second function and places
it at the top of the stack, making it the active context.
The second function becomes the active context, the variable b = 'Hey!';
gets store in its FEC, and the third function is invoked within the second
function. Its FEC is created and put on top of the execution stack.

Inside of the third function the variable c = 'Hello!' gets stored in its FEC
and the message Hello! Victor gets logged to the console.
Hence the function has performed all its tasks and we say it returns . Its
FEC gets removed from the top of the stack and the FEC of the second
function which called the third function gets back to being the active
context.

Back in the second function, the message Hey! Victor gets logged to the
console. The function completes its task, returns , and its Execution
Context gets popped off the call stack.
When the first function gets executed completely, the execution stack of
the first function popped out from the stack. Hence, the control reaches
back to the GEC of the code.

And lastly, when the execution of the entire code gets completed, the JS
engine removes the GEC from the current stack.

Global Execution Context VS. Function


Execution Context in JavaScript
Since you've read all the way until this section, let's summarize the key
points between the GEC and the FEC with the table below.

GLOBAL EXECUTION
Function Execution Context
CONTEXT

Creates a Global Variable object


Doesn't create a Global Variable
that stores function and variables
object. Rather, it creates an
declarations.
argument object that stores all
the arguments passed to the
Creates the `this` object that function.
stores all the variables and Doesn't create the `this` object,
functions in the Global scope as
but has access to that of the
methods and properties.
environment in which it is
Can't access the code of the defined. Usually the
Function contexts defined in it `window` object.

Due to scoping, has access to


the code(variables and
Sets up memory space for functions) in the context it is
variables and functions defined defined and that of its parents
globally
Sets up memory space only for
variables and functions defined
within the function.
Conclusion
JavaScript's Execution Context is the basis for understanding many other
fundamental concepts correctly.

The Execution Context (GEC and FEC), and the call stack are the
processes carried out under the hood by the JS engine that let our code
run.

Hope now you have a better understanding in which order your


functions/code run and how JavaScript Engine treats them.

As a developer, having a good understanding of these concepts helps you:

Get a decent understanding of the ins and outs of the

language. Get a good grasp of a language’s underlying/core

concepts.

Write clean, maintainable, and well-structured code, introducing


fewer bugs into production.

All this will make you a better developer overall.

Hope you found this article helpful. Do share it with your friends and
network, and feel free to connect with me on Twitter and my blog where I
share a wide range of free educational articles and resources. This really
motivates me to publish more.

Thanks for reading, and happy coding!


ADVERTISEMENT

freeCodeCamp.org © 2022

Short Series CrushedS2 I Free


Best romantic show on miniTV - Crushed S2 is here! Watch on Amazon shopping app for free!

You might also like