Before we start about JS engine, let’s understand about what JavaScript runtime environment is and where the JavaScript engine works to understand how it works. JavaScript can run anywhere in browsers, servers, smart watches, robots, water purifiers, literally anywhere and this is possible because of JavaScript Runtime environment.
This JavaScript environment does everything that is required for a piece of JavaScript code to be taken in as an input, processed and give the required output. It contains the JS engine, APIs (to connect to the web), Event Loop, callstack queue, microstack queue.
So, coming to browsers, any given browser requires a JavaScript runtime environment to execute JavaScript code and this is where the JS engine picks its role to run the code in the Run time environment which also helps in the interaction with APIs, hence is the most important component of the JS Runtime environment. There are tones of JS engines like Chakra used in Microsoft edge, Spidermonkey used in Mozilla and V8 used in Google chrome. One can build a JS engine by themselves by following ECMAScript.
JS engine is just a piece of code written in assembly level language that takes in normal JavaScript code and converts it into a machine level code.
How does it do that?
The code which is taken in as an input undergoes three major steps for execution,
1. Parsing
2. Compilation
3. Execution
Parsing➡
During parsing the high-level JavaScript code is taken in as an input and is broken-down into tokens.
Then these tokens are sent to a syntax parser which converts the code into an AST (Abstract Syntax Tree).
here is an example for an AST, Yup, this the AST of just one line of code🤡
Compilation🏭
Before diving into compilation let’s briefly understand what is an interpreter and compiler. Interpreter - Executes code line by line by line in the order i.e. synchronously, hence the execution is fast. Compiler - The entire code is compiled and creates an optimized version of the code which is then executed, hence it is more efficient.
When JS was invented it was supposed to be interpreted, but now it is both an interpreter + compiler. JavaScript can behave as both interpreted and compiled language based on the engine which decides if it should use interpreter or compiler or instead both which is known as JIT.
JIT (Just in Time) compilation uses both compiler and interpreter. Hence, compilation and execution go hand-in-hand. As the AST that is created in parsing is sent to interpreter which converts the high-level code into byte code and sends it for execution but while the code is interpreted line-by-line by the interpreter, it also interacts with compiler which optimizes the code and take codes from it. Hence the entire execution happens in the multiple phases. The compiler tries to optimize on the run time, hence it is known as JIT compilation. In some engines there is AOT (Ahead of Time) compilation where the compiler takes the code in the later part and compiles it and converts it to byte code and sends it to the execution phase.
Execution✨
The two main components of execution phase happen using the memory heap and the call stack.
Memory heap - place where things like variables, functions are stored and is in sync with the call stack and garbage collector and etc.
Call stack - maintains the order of execution context in the JavaScript run time environment. Execution also has garbage collector which removes, stores and maintains memory using Mark and sweep and other algorithms.
This is how a basic and generic JS engine works in any browser🎈!!
Overview👀
To run JavaScript code anywhere we need a JS Runtime Environment which contains the JS engine. In the JavaScript engine, the code which is taken as input undergoes three phases. Firstly, parsing in which the code is converted into tokens and with the help of syntax parser, the AST is generated which is passed to the next phase that is compilation where the JIT compilation occurs where an interpreter and compiler work together with the help of multiple optimization techniques to the code and convert it into an optimized efficient byte code and sends to the execution phase which is the last phase in which the byte code is executed with the help of memory heap and the call stack.
And there you go🎉 now you know how exactly JavaScript code is read, processed and executed in the JavaScript run time environment and make it do what it does!