Wednesday, October 9, 2024

What is the JavaScript Engine and How It Works

Recently, we started a series aimed at teaching web developers how JavaScript works and what the different components that make the process of code compilation and execution in JavaScript fast and simple.

The first article of this series was an overview of the JavaScript engine, the call stack, and the runtime environment. In this second part of this JavaScript tutorial series, we will focus on internal parts of the JavaScript engine and uncover why JavaScript is no longer an interpreted programming language.

If you missed it or need a refresher, be sure to read the first part of our series: How JavaScript Works Behind the Scenes.

What is the JavaScript Engine?

The JavaScript Engine is a program whose responsibility is to execute JavaScript code. All modern browsers come with their own version of the JavaScript Engine but the most popular one is Google’s V8 Engine. Google’s V8 engine powers Google Chrome browsers, as well as, Node.js. Node.js is a JavaScript runtime that is used to build server-side applications outside of the browser.

Here is a list of the different JavaScript Engines for each major Internet browser:

  • V8 – Open-source JavaScript Engine developed by Google for Chrome
  • SpiderMonkey – The JavaScript Engine powering Mozilla Firefox
  • JavaScriptCore – Open-source JavaScript Engine developed by Apple for Safari
  • Rhino – Open-source JavaScript Engine managed by Mozilla foundation for FireFox
  • Chakra – A JavaScript Engine for Microsoft Edge
  • JerryScript – A JavaScript engine for the Internet of Things (Iot).

Now that we understand what a JavaScript Engine is, we can take a deeper peek under the hood and learn about the different components of JavaScript. So, with this look at the engine behind us, in the next section, we will discuss how JavaScript code is compiled into machine code so it can be executed.

Read: Top 10 AngularJS Alternatives

How Does JavaScript Compilation and Interpretation Work?

First, let’s understand the differences between a compiler and an interpreter. As web developers may know, a computer can only understand 0s and 1s (think of them as simple on and off switches). That is why every computer program ultimately needs to be converted into machine code. This task is performed using a process either known as compilation or interpretation. We look at each of these processes in the next section.

What is Compilation in Programming?

During compilation, the entire source code gets converted into machine code all at once. The machine code is written into a portable file that can be executed anywhere – regardless of platform or operating system. There are two steps involved in the code compilation process. In the first step, the machine code is built and in the second step, it is executed on the machine.

The execution of machine code happens right after the compilation. For example, any application you are now using on your computer has been compiled first and you are now able to execute it on your machine.

How JavaScript compilation works

Read: Overview of Garbage Collection in JavaScript

What is Interpretation in Programming?

On the other hand, during interpretation, the interpreter runs through the source code and executes it line by line. Unlike compilation, which involves a two-step process, in interpretation, the code is read and executed at the same time. Of course, the source code still needs to be converted into machine language, but the conversion of code does not happen ahead of time, but, instead, right before execution.

JavaScript interpretation example

What is Just-in-Time Compilation?

JavaScript is a purely interpreted language. The problem with being an interpreted language, however, is that interpreted programming languages are much slower in terms of performance when compared to compiled languages. This represents a problem for modern applications that require fast processing and high performance. Just imagine that you are playing an online game on your browser, and, when you move a cube through a tile, it takes several seconds to reach the endpoint. Would such slowness be acceptable?

Many people still call JavaScript an interpreted programming language but that is no longer the case. Current JavaScript Engine use the concept of both compilation and interpretation at the same time, which is known as Just-in-Time (JIT) compilation.

In this approach, the whole source code gets compiled into machine language at once and then it is executed. There are still two steps involved in the ahead of time compilation, but there is no portable file to execute. This is a perfect solution for today’s fast performance demanding web applications, as this process is much faster than just executing the code line by line. This inclusion of JIT compilation is the reason JavaScript is, technically, no longer an interpreted programming language.

How Does Just-in-Time Compilation (JIT) Work?

So, how does the JavaScript Just-in-Time compiler work? As a new piece of JavaScript code enters the JavaScript Engine, the first step performed is parsing of the code. During this process, the code is parsed into a data structure called the Abstract Syntax Tree (AST).

The Abstract Syntax Tree first splits each line of code into pieces that are meaningful to JavaScript, such as the let, static, or function keywords. It then saves these pieces of code into a tree-like structure. The next step checks whether there are any syntax errors and, if none are found, the resulting tree is used to generate the machine code.

Now, let’s take a look at a quick example of JIT in action. In the following JavaScript code snippet, we have declared a variable on the left side, and, on the right side, there is its equivalent AST:

JIT Compilation explained JavaScript JIT Example

Here, we have declared a const variable with the name val and given it the value 45. As you can see on the AST tree side, besides the declaration, there is a lot of additional code. Can you imagine all of the extraneous code that would be generated in a large application?

In the next step, the generated AST is compiled into machine code. Then this machine language gets executed. This is how modern JavaScript uses Just-in-time compilation. Remember, the machine code execution process happens in the JavaScript Engine’s call stack.

JavaScript Call Stack

So far so good – at this point, our code is running and that should be the end of the process. Not so fast – JavaScript has some code optimization strategies to implement. In the beginning, JavaScript creates a very un-optimized machine code so that it can execute the scripts as fast as possible. Then, in the background, this un-optimized code gets recompiled and optimized, while the current code is executed. This is done most of the time and after each cycle of optimization, the un-optimized code gets exchanged for the more optimized code, without ever halting the execution. That is why the JavaScript Engine works so fast.

The process of code optimization happens in special threads, separate from the main thread. JavaScript developers cannot access the code optimization algorithm from our source code. Although different engines implement the strategy in different ways, in a nutshell, this is how modern JavaScript Just-in-time compilation works.

Read: HTML, CSS, and JavaScript Tools and Libraries

Tariq Siddiqui
Tariq Siddiqui
A graduate in MS Computer Applications and a Web Developer from India with diverse skills across multiple Web development technologies. Enjoys writing about any tech topic, including programming, algorithms and cloud computing. Traveling and playing video games are the hobbies that interest me most.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured