Purpose of the Article: Learn internal and critical concepts of JavaScript in-depth for a strong foundation
Intended Audience: JavaScript Developers
Tools and Technology: JavaScript, Google Chrome, Mozilla Firefox, Apple Safari, and Microsoft Edge
Keywords: JavaScript, JavaScript Engine, JavaScript Runtime Environment, Angular, React, Vue, Node
“JavaScript is an asynchronous single-threaded language that can be non-blocking.”
JavaScript can run inside a browser, inside a server, and inside a smartphone, etc. How is that all possible? It’s possible because of the JavaScript Runtime Environment.
JavaScript Runtime Environment
To run any piece of JavaScript code, we will need a JavaScript Engine.
JavaScript Runtime Environment also contains, set of Web APIs to connect to the outer environment, a callback queue, and an event loop.
For example, the JavaScript Runtime Environment inside the browser has an API known as local storage, allowing us to access our code’s local storage. A few APIs have the same name inside the browser and node JS like set Timeout and console. But internally, their implementations might be different.
Every browser has its JavaScript engine. Refer to the screenshot below for examples.
You can create your JavaScript engine. The most crucial protocol is to follow the ECMAScript Language Standards.
The first-ever JavaScript engine was created by Brendan Eich, the creator of JavaScript himself in 1995, for the Netscape Navigator web browser. It was a pure interpreter and eventually evolved into SpiderMonkey and is still used in Firefox browsers.
Node JS is an open-source JavaScript runtime built on Chrome’s V8 JavaScript engine. So, this allows us to execute JavaScript code outside of the browser.
JavaScript Engine Architecture
JavaScript engine takes the human-readable code and processes it in three significant steps.
- Parsing
- Compilation
- Execution
Parsing
During this parsing phase, the code which we write is broken down into Tokens. For example, let a=0; let is one token, a is one token, = is one token, and so on. There is a syntax parser whose job is to take the code and convert it into AST (Abstract Syntax Tree). The resultant AST is passed to the compilation phase.
Compilation
Is JavaScript an interpreted language or a compilation language? First, let’s see what’s an interpreter is. In many interpreted programming languages, the code is executed using an interpreter. It executes line by line in order. It does not know what will happen in the following line, and that’s how the code is executed.
While on the other hand, many programming languages use the compiler to compile the code. So, in the case of a compiler, the whole code is compiled to its optimized version first, and then it starts executing.
Both have pros and cons. In an interpreter, the code is FAST. It need not wait for the code to be compiled first. It immediately starts executing the code line by line. In the case of compilers, we have EFFICIENCY.
JavaScript can behave like an interpreted language as well as a compiled language. It’s all dependent on the JavaScript engine. When JavaScript was initially created, it was an interpreted language because it was majorly used in browsers, and browsers can’t wait for the compilation to be completed. It must be fast, so back then, it was an interpreted language.
In today’s modern world, most of the JS engines you see use the interpreter and a compiler both together. So, it is purely dependent on the JS engine, whether it is purely interpreted or just-in-time compiled. There is an exciting jargon here, Just-In-Time compilation. The JavaScript engines can use an interpreter along with a compiler, making it a Just-In-Time compiled language. JIT compilation is like the best of both worlds. It uses the interpreter + compiler to execute the code.
So, when we get the AST from the parsing phase to the compilation phase, it goes to the interpreter, then the interpreter converts the high-level code into the Bytecode and sends it to the execution step. The compiler talks to the interpreter while interpreting the code line by line, simultaneously optimizing the code as much as possible. So, it’s not just one phase process; it happens in multiple phases. All these JavaScript engines have their algorithms for doing it.
In some JavaScript engines, there is a mechanism known as AOT (Ahead of Time). In this case, the compiler takes a piece of code and tries to optimize it before execution.
Execution
There are two essential components in the execution phase.
- Memory heap
- Call stack
Memory heap is where the memory is allocated to the variables and functions. A garbage collector frees up the memory whenever a variable or function is no longer required. So, this is done by using an algorithm called Mark & Sweep algorithm. It is used in a wide range of garbage collectors, not only in JavaScript.
Call stack, whenever a function is invoked, it will be placed in the call stack to execute, and the functions are executed in LIFO (Last In First Out) manner. The event loop will continuously keep checking the call stack, whether it is empty or not. If it is empty and the callback queue has something waiting to be executed, the event loop will push it to the call stack, and this continues until there is nothing left to be executed.
As of today, Google’s V8 engine is the fastest among all other JavaScript engines out there.
Google V8 Architecture
Google has named its interpreter as Ignition and compiler as TurboFan.
The JavaScript Source Code is initially picked up by Parser and broken down into tokens passed to create AST (Abstract Syntax Tree).
AST is sent to Ignition Interpreter and starts interpreting line by line to convert into Bytecode. Simultaneously, the TurboFan compiler will optimize the code as much as possible and update the Bytecode, which is further passed to the execution phase.
I hope this was useful and helped you to understand the essential concepts of JavaScript fundamentals.
References / Information Sources:
- You don’t know JS (Book Series) by Kyle Simpson
- https://v8.dev
- https://spidermonkey.dev
- https://developer.apple.com/documentation/javascriptcore