App LogoGithub Logo

How JavaScript Works?

27 February 2024

When you run your JavaScript code, it compiles and gets executed in your browser or server. How does your JavaScript code compile into machine code? Let's dive a little deeper to learn more about how JavaScript works.

What is JS Engine?

A JS Engine is a piece of program that executes your JS code. So that means JS can execute not only on a browser but also on any piece of device which has a JS Engine. Every browser has its own version of a JS Engine. For Example...

  • V8 in Chrome, Opera, Edge and even Node
  • Chakra in IE
  • Spider Monkey in FireFox

Earlier, JS was only limited to running on browsers as a client-side language only. With Node.js, JS can also be used on the server, and to execute JS, Node.js uses the V8 engine. In simple terms,


Node JS = V8Engine + Node API/Modules
JS Engine
Any JS Engine contain two main components
  • Call Stack: A call stack is where your code gets executed inside the Execution Context.
  • Heap: A heap is an unstructured memory pool, which stores all the objects that your application needs.

[Fig 1.1] JS Engine

Call stack & Execution Context

An Execution context (EC) is where your JS code will get executed, so all the necessary information about your code being run will be stored inside the EC. Each EC has two phases.

  • Creation Phase
  • Execution Phase

The Creation Phase

When the JS Engine executes your JS Code for the first time, before even executing your code, it will create the Global Execution Context which is default EC and contain top-level code. During this Phase JS Engine will do following tasks:

  • Create a global object: window object in browser & global object in Node.
  • Bind this to global object.
  • Creation of Scope Chain.
  • Store function declaration in memory and initialize the variables with undefined inside GEC.

So each Execution Context store:

  • A Variable Environment
    • let, const, var declaration.
    • function and arguments.
  • A Scope Chain.
  • this keyword.

The Execution Phase

During the execution phase, the Engine will run JS code line by line, assign values to variables, and execute functions. For each function call, the JS Engine will create a new context, known as the Function Execution Context, and push it into the call stack. Unlike the Global EC, the Function EC represents the function's local scope, so it will only have local variables.


A brief intro of V8 Engine

V8 Engine

The JS file enters the engine and the parser parses the file, performing lexical analysis to break the code into tokens, which will be used in the creation of an Abstract Syntax Tree (AST).

Now, the generated AST is passed down to the interpreter, which generates the unoptimized code, allowing execution to start with no delay. The profilerwatches the code as it runs and identifies areas where optimization is needed. The profiler then passes that code to the compiler which performs optimization and generates optimized code that can replace its counterpart in the non-optimized code generated by the interpreter.

As the profiler and compiler constantly make changes to the bytecode, the JavaScript execution performance gradually improves.

[Fig 1.2] V8 Engine

In V8 Engine, JS is interpreted by an interpreter called Ignition as well as compiled by a JIT-optimized compiler called TurboFan

What is JS Runtime?

JS Engine runs inside an environment, which provides additional features to the scripts that we can use at runtime.

JS Runtime
  1. JS scripts get access to Web APIs, which are not part of native JS. For example, DOM manipulation, AJAX requests, setTimeout, etc.
  2. JS runtime also includes the Event loop and Callback queue.
    • When scripts start running, first all the synchronous code executes. All the callbacks associated with some event are queued in the callback queue.
    • Now, the job of the event loop is to take the callback function that is ready to execute and, if the call stack is empty, put the callbacks into the call stack for execution.

[Fig 1.3] JS Runtime

Example

Let's understand the working of JS Runtime via example:

JS Runtime Example GIF

[Fig 1.4] JS Runtime Visualize via loupe

  1. Once the code starts executing, first the console.log() function will be pushed onto the call stack. Once it's executed, it will be popped out of the stack.
  2. Next, the run() function will be pushed onto the stack, and execution will start. setTimeout(), provided by the WebAPI, delays tasks without blocking the main thread. So, the callback function passed to setTimeout will be handled by the WebAPI, and the timer will start running while the main thread continues executing and moves to the next console.log() for execution.
    • In the example, the time being 0 doesn't mean it will be executed or added to the call stack after that time. What it means is that after x time, it will be added to the queue for further execution.
  3. The run() function will be popped out of the stack, and the last console.log() will be pushed onto the stack for execution.
  4. Now, your synchronous code execution is done, but what about the setTimeout callback? How will it get executed? That's where the event loop comes into the picture. The job of the event loop is to continue monitoring the call stack. Once it's empty, it takes tasks from the queue and puts them back into the call stack for execution.
    • So, once the last console.log() execution is done and the call stack is empty, the event loop will take the callback from the queue and put it back into the call stack for execution.

Last Updated

27 February 2024
0