☝️ First things first, what's an environment?
Can you recall the list of things that you consume in code, and haven't actually defined them anywhere, or ever wondered how this language runs asynchronous code despite being single thread?
So that is provided to you by the runtime environment, things like the DOM, that beautiful setTimeout() function etc, are all provided by the environment.
We call it the javascript runtime environment.
Imagine it like a big container, and the engine we are interested in, is a part of this container.
The Javascript runtime environment(JRE) is responsible for making JavaScript asynchronous. It is the reason JavaScript is able to add event listeners and make HTTP requests asynchronously.
JRE consists of the following components:
- JS Engine
- Web API
- Callback Queue or message queue
- Event Table
- Event loop
For browsers (client side ): JRE differs from browser to browser although this difference used to be huge back in 2000s, but has greatly reduced now.
Node.js according to the official documentations is a server side JavaScript runtime, which means it would differ from client side environments.So things like DOM won't be there in the Node.js environment.
Also 💥Deno💥, is a runtime environment for Typescript and Javascript. Remember that in other Javascript environments Typescript is supported, but in them (eg Node.js) Typescript is first transpiled into Javascript
The Javascript Engine
JavaScript engines are inbuilt in all the modern browsers today. When the JavaScript file is loaded in the browser, JavaScript engine will execute each line of the file from top to bottom (to simplify the explanation we are avoiding hoisting in JS). JavaScript engine will parse the code line by line, convert it into machine code and then execute it.
JavaScript engine consists of two components:
- Execution context stack (process data)
- Heap (save data)
Execution context stack :
Execution context stack is a stack data structure.As JavaScript engine has only one ECS, it can execute only one thing at a time which is at the top of the ECS. This is what makes JavaScript single threaded. If the same function is called twice like in recursion, it will have two different functional execution context in the ECS.
Heap :
Heap is a large unstructured data structure which stores all the dynamic data like function definitions, objects, arrays etc. Execution context stack just contains their reference or in other words stores their memory addresses where these function definitions, objects and arrays are stored. The memory occupied in the heap continues to exist even after the JavaScript code execution has completed. They are removed by the JavaScript Garbage Collector.
🔥 By default, at the bottom of the ECS, we have a global execution context which deals with all the code in the global scope (the functions you can access anywhere). Also Each function has its own execution context called functional execution context (the data in this context can be only consumed by the function itself, and it's children functions) which gets inserted on the top of ECS as and when the function is called in the code.
🔥 Something from real life?
Imagine a robot is putting out a fire:
- The JavaScript code would be the instructions to the robot to put out a fire.
- The JavaScript engine would be the robot which can understand the instructions and act on it.
- The JavaScript runtime would be the fire truck, and the water gun.
How does the engine work?
function functionOne() {
console.log('function 1 called');
functionTwo();
}
function functionTwo() {
console.log('function 2 called');
}
functionOne();
When the browser loads the JS file, the JS engine will push the global execution context in the ECS and will start executing it. When JS engine reaches the function definition of functionOne, it stores the function definition in the heap memory and its reference in the global execution context. When functionOne is called by JS engine, it pushes functionOne execution context inside the ECS and starts executing functionOne, pausing the execution of the global execution context.
👉 When JS engine calls functioninTwo inside functionOne, JS engine pushes functionTwo inside ECS and starts executing functionTwo, pausing the execution of functionOne.
👉 Once all the code inside functionTwo is executed, JS engine pop out functionTwo execution context and restarts executing remaining code of functionOne.
👉 Similarly, it removes the functionOne execution context once all the code of functionOne is executed.
👉 It should be noted that even though functionOne has been removed from the ECS, objects and function definitions inside functionOne continue to occupy memory in the heap without being referred by any variable. They will be removed by the garbage collector automatically, we don't have to remove it ourselves.
I think that should have given you a good idea of the JS engines. Let me know if we should see what's inside Google's V8 engine, or the SpiderMonkey from Mozilla.Cheers 😊
References :
JavaScript Internals
The JS runtime environment
Top comments (1)
Bookmarked ✌️