Execution context in JS

📄 Table of Contents

  • Hoisting
  • HTTP Caching — Part I

Execution context, Scope chain and JavaScript internals

Execution context (EC) is defined as the environment in which the JavaScript code is executed. By environment, I mean the value of this, variables, objects, and functions JavaScript code has access to at a particular time.

Execution context stack (ECS): Execution context stack is a stack (LIFO) data structure, i.e. last in first out data structure, to store all the execution stacks created during the life cycle of the script.

Execution context in JavaScript is of 3 types as:

  1. Global execution context (GEC): This is the default execution context in which JS code start its execution when the file first loads in the browser. All of the global code i.e. code which is not inside any function or object is executed inside the global execution context. GEC cannot be more than one because only one global environment is possible for JS code execution as the JS engine is single threaded.
  2. Functional execution context (FEC): Functional execution context is defined as the context created by the JS engine whenever it finds any function call. Each function has its own execution context. It can be more than one. Functional execution context has access to all the code of the global execution context though vice versa is not applicable. While executing the global execution context code, if JS engine finds a function call, it creates a new functional execution context for that function. In the browser context, if the code is executing in strict mode value of this is undefined else it is window object in the function execution context.
  3. Eval: Execution context inside eval function.

Global execution context is present by default in execution context stack and it is at the bottom of the stack. While executing the global execution context code, if JS engines find a function call, it creates a functional execution context for that function and pushes it on top of the execution context stack. JS engine executes the function whose execution context is at the top of the execution context stack. Once all the code of the function is executed, JS engines pop out that function’s execution context and start’s executing the function which is below it.

Let’s understand this with the help of an example:

As soon as the above code loads into the browser, JS engine pushes the global execution context in the execution context stack. When functionA is called from global execution context, JS engine pushes functionA execution context in the execution context stack and starts executing functionA.

When functionB is called from functionA execution context, JS engine pushes functionB execution context in the execution context stack. Once all the code of functionB gets executed, JS engine pops out functionB execution context. After this, as functionA execution context is on top of the execution context stack, JS engine starts executing the remaining code of functionA.

Once all the code from functionA gets executed, JS engine pops out functionA execution context from execution context stack and starts executing remaining code from the global execution context.

When all the code is executed JS engine pops out the global execution context and execution of JavaScript ends.

So far we have discussed how the JavaScript engine handles the execution context. Now, we will see how it creates the execution context.

Understanding “this” binding in a normal Javascript function and ES6 Arrow function + execution Context

Arrow syntax automatically binds this to the surrounding code’s context.

Here, the Person function is a constructor function. We are creating an object person1 by the new keyword.

var person1 = new Person('Joey Smith', 25);
  1. When this line is executed the constructor function Person following the new keyword is invoked, which sets the attributes of the empty object that is newly created using this.property_name which includes personName, age, and function getDetails. This constructor implicitly returns an object which is stored in the person1 variable.
  2. Then we can invoke the member function getDetails of the person1 object to console the details of the person by the execution of the following line of code.
person1.getDetails()

3. The getDetails have a setTimeout which will execute the function passed in it after the specified time (time is passed as the second argument to the setTimeout function). The callback passed in setTimeout is the normal function in which this is being referred.

When the above code is executed the following output is generated:

Name: undefined, Age: undefined

The name and age both got undefined. Oopsie!! That’s not what you must have expected.

Why is that?
The reason for the undesired output is that in normal functions this keyword represents the object that has called the function. When our function getDetails is normally invoked this keyword represents the global object that is the window object in our case and there is no variable with the name personName or age in the global object, due to which both of these are undefined.

But what’s happening in the execution stack?

First of all the Global Execution Context enters the Execution Stack which has a constructor function Person and person1 object in its variable environment. The Person object is created via new keyword by the steps explained above and then stored in the person.

Execution Stack

After that, the Global Execution Context invokes the getDetails function which is a member function of the person1 object. When the getDetails function is invoked, its execution context is created and pushed onto the Execution Stack.

Execution Stack

The getDetails function has the setTimeout which has a callback that will enter the Callback Queue (just assume its a storage where callbacks are put ) and will be executed once our execution context other than global Execution Context is popped out from the Execution Context.

The left side is execution Context and on the right side is the setTimeout function waiting for the specified time to pass

When the stack gets empty (except global Execution Context) the callback will be executed and a new Execution Context will be made. i.e. the callback is invoked normally and this keyword in it represents the Global Context.

Execution Stack

In the case of a normally invoked function, this keyword represents the global Context and when personName and age variable is not found in the Global Context it gives undefined.

So this is our problem that while normal function invocation the this represents the object that has called the function, not the surrounding context itself.

How to solve this?

All the functions in JavaScript have access to some special methods on their own and bind(), call() and apply() are few of them. One the solution is using these bind(), call() or apply(). By using these methods we can control, what this ends up representing.

Therefore by using these methods we can bind the Person to the callback function and then our problem will be solved. Following is the example of using a bind method on the callback function and binding it to the Person.

Similarly, we can use apply and call method.

But why going through so much trouble when the problem can be solved just by syntax change.

In such a scenario, Arrow Function comes to rescue with its so easy syntax.

If we use the arrow function as callback instead of the normal function our problem will be gone. Let’s see the code.

We get the following output on executing the code.

Name: Chandler, Age: 25

Basically in the Arrow function, this always represents the object in which the arrow function is defined. Arrow syntax automatically binds this to the surrounding code’s context under the hood.

In the Arrow function, it is not dependent on how they are invoked but it closes on the surrounding context.

Hoisting

In fact, there are two ways to declare a function with the function keyword. One way is using function declarations, which are also called function statements, and the other is to use function expressions.

In JavaScript, functions are first-class, which means that they’re objects we can define on the fly.

A function declaration is stored in memory prior to executing the program. This means we can reference it anywhere in our code, even before it’s declared. Storing a variable in memory prior to executing the program is called “hoisting” in JavaScript.

For example, if we write the following:

console.log(foo());function foo() {
return 'foo'
}
console.log(foo());

We get foo logged from both function calls.

Unless they’re function declarations or statements, function expressions aren’t stored in memory before the program runs. The function is defined in run-time, so we can only reference them after they’re declared.

For example, if write the follwing:

console.log(foo());const foo = function() {
return 'foo'
};
console.log(foo());

We’ll get the following error: Uncaught ReferenceError: Cannot access ‘foo’ before initialization’.

In the code above, we have the variable declaration when the program initially runs, but we don’t have the function assigned to it yet, so we can’t access it. Also, anything defined with the let , or const keywords can’t be used with those variables or constants before it’s defined, which also prevents it from being run.

HTTP Caching

Caching, in general is a mechanism to prevent fetching the same resource, if unchanged again and again costing data and affecting performance. The retrieval of previously fetched resource can be prevented by storing the data locally and verifying the presence of a resource locally before making any other request.

This can be achieved by passing some special cache directives in the response headers of an HTTP request.

Cache-control: This is the caching directive that is used to specify who can cache a resource and how long one can cache it. This takes the values

  • max-age: This takes the value of the time period for which the resource is valid and can be reused from the cache.
  • public or private: “public” lets the resource to be cached even if it has HTTP authentication associated with it. A resource with “private” in the cache-control directive prevents intermediate caching and allows only the end user to cache it.
  • no-cache or no-store: “no-cache”means the client must validate the freshness of a resource even though the resource is cached. “no-store” disallows the browser and all intermediate caches from storing any response.

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store