Namastey🙏 to all the readers, Let's start the article with a positive note👇
Have you ever wondered🤔 how the javascript code we write runs on the machine. If you don't know no problem, today we will discuss it in this article. In this article, we will discuss how the Javascript code gets converted into machine code so that it can run on the machine. As a javascript developer, having a deeper understanding of how Javascript code runs on the machine helps you to write efficient Javascript code.
What we will cover in this article
Explanation of Javascript runtime environment.
Working of Javascript engine
Popular Javascript engines
Explanation of V8 Javascript engine
Explanation of SpiderMonkey Javascript Engine
Explanation of Javascript runtime environment
JavascriptEngine alone can't run the Javascript code. For example, we can't run asynchronous javascript code directly inside the javascript engine, we need some components like WebAPI environment, event loop, callback queue, and microtask queue. All the above components worked together inside a container known as Javascript Runtime Environment(JRE). So to run a piece of Javascript code inside any machine we need a Javascript runtime environment. In the case of the web the JRE is browser and outside of the web, the JRE is Node js.
Now we will briefly understand the components of JRE
Javascript Engine
What you think about Javascript engine Like this👇 No, it is not hardware instead it is also software that is written by developers like us. It is responsible to convert Javascript code to optimized machine code and then execute it. If you are not familiar with machine code then just remember that it is the language that CPU💻 understands. Machinecode👇
We will understand the Javascript engine in-depth in the coming sections of the article.
WebAPI
It helps in performing asynchronous operations in Javascript. It provides access to functions performing asynchronous operations inside Javascript code.
Callback queue
Stores the callback associated with WebAPI.
Microtask queue
Store those callbacks coming from promises and mutation observers.
Event loop
The above callback coming from either callback or microtask queue is provided to Javascript engine with the help of Event loop.
Below is the block diagram of the javascript runtime environment created after connecting all the components discussed above. If you are curious in reading more about asynchronous Javascript, callback queue, event loop, and WebAPI then read this article.
Now we are familiar with Javascript engine and Javascript runtime environment so let's now understand the working of Javascript engine.
Working of Javascript engine
First, we will understand each component of the Javascript engine. To understand the working of the Javascript engine we will use the below javascript code snippet which is also used in the latter part of the article to explain different topics.
function add(a, b) {
return a + b;
}
add(2, 3);
Intialisation environment
This component is used to initialize the Javascript runtime environment with the help of the renderer process. Each browser tab has a renderer process.
Parser
It is responsible to convert javascript code into an abstract syntax tree. The code in the above code snippet is broken down into tokens.
After breaking the code into tokens it is passed to syntax parser which converts it into an Abstract Syntax Tree(AST). It is a tree representation of javascript code. Below is the Abstract syntax tree generated for the above code snippet
Interpreter
The interpreter receives the AST then analyzes the code line by line and converts it into bytecode. Command to generate bytecode in node js for a particular function, in our context for add function
node --print-bytecode --print-bytecode-filter=<function name> <javascript filename>
Below is the generated bytecode for the above code snippet
Optimizing compiler
To make Javascript code run faster, the bytecode can be sent to the optimizing compiler along with profiling data. The optimizing compiler makes certain assumptions based on the profiling data it has and then produces highly-optimized machine code. If at some point one of the assumptions turns out to be incorrect, the optimizing compiler deoptimizes and goes back to the interpreter.
Execution
The bytecode is executed by using the Memory heap and the Callstack. The memory heap is the place where all the functions and variables are assigned to memory. Callstack is the place where each individual functions, when called are pushed to the stack and popped out after their execution. Any unused memory found during the program is freed by the garbage collector
After understanding each component we are in a better position to understand the full picture of the javascript engine. Below is the high-level block diagram of the javascript engine.
Summarising the working of the javascript engine with the help of the following steps:-
The parser converts the javascript code into an abstract syntax tree.
Interpreter takes the abstract syntax tree as input and converts it into bytecode.
Optimizing compiler optimizes bytecode based on certain assumptions by using profiling data and deoptimizes if the assumption is wrong
The bytecode generated by the interpreter is finally executed by the callstack and memory heap.
We have seen the working of the javascript engine. Now, let's check what are the popular JavaScript engines available.
Popular javascript engines
V8 - V8 is an open-source engine developed by Google and it is written in C++. It is used inside Google chrome,Node.js and Deno.
Spider Monkey - It is the first javascript engine created by Brendan Eich founder of javascript. Initially, it powered the Netscape Navigator browser and today powers Firefox and SpiderNode.
Chakra - It is Microsoft's javascript engine used in Microsoft Edge browser and Node-ChakraCore.
JavaScriptCore - It is an open-source engine developed by Apple for the Safari browser.
JerryScript - It is a lightweight engine that is used in applications based on the Internet of Things.
In this article, we will discuss only V8 and SpiderMonkey as V8 is the fastest javascript engine available today and SpiderMonkey is the first javascript engine.
V8 Javascript Engine
V8 compiles Javascript code into machine code at execution by implementing a JIT(Just-In-Time) compiler. The V8 engine initially uses an interpreter, to interpret the code. On further executions, the V8 engine finds patterns such as frequently executed functions, frequently used variables, and compiles them to improve performance. Suppose the performance degrades or the parameters passed to the function change their type, then the V8 simply decompiles the compiled code and falls back to the interpreter.
Working of the V8 engine
When V8 compiles Javascript code, the parser generates an Abstract syntax tree. The AST is passed to the ignition interpreter which is responsible for generating and executing bytecode. While it runs the bytecode, it collects profiling data, which can be used to speed up the execution later. When a function becomes hot i.e when it's run often, the generated bytecode and the profiling data are passed on to TurboFan optimizing compiler, which generates highly optimized machine code based on the profiling data and feedback. The V8 engine is provided with the Orinoco Garbage Collector which internally uses the Mark and Sweep Algorithm to free up space from the memory heap.
How the TurboFan compiler generates optimized machine code?
The compiler uses the Inline caching technique for optimization. Inline caching is a technique used to prevent generating bytecode for repeated function calls. Check below code snippet to understand inline caching
Now to further strengthen our knowledge of javascript engines, we will also look into another javascript engine.
Spider Monkey javascript engine
The working of Spider Monkey javascript engine is mostly the same as V8 but instead of using one optimizing compiler, it uses two optimizing compilers. The baseline compiler optimizes those portions of bytecode that are marked as hot . The generated optimized code is passed to the IonMonkey compiler with profiling data to generate highly optimized code. If the assumption made for optimization by the IonMonkey compiler fails, it falls back to the baseline compiler.
Conclusion
Congratulations on reading until the end👍
In this article, you've learned
- what is the Javascript runtime environment and explanation of its different components
- what is Javascript engine and a block diagram to explain it
- V8 javascript engine explanation
- Spider monkey javascript engine explanation
I hope you enjoyed😃 reading the article. Thanks for reading!!
Please provide feedback on the content of the article. You can connect with me on social media👇
For further information related to this article check below resources👇