JavaScript is generally considered as interpreted language, although it has characteristics of both interpreted and complied language. Modern JS engines like google V8 , node JS uses just-in-time compilation to achieve benefits of compilation. Let’s see how ?
JavaScript execution
There are multiple stages involved in JavaScript code execution.
Parsing
In this stage JS engines parse JavaScript code, during parsing declarations are hoisted to the top of their scopes.
Js engine reads all the code and breaks it into a data structure called the Abstract syntax tree.
AST generation
After parsing, Js engine generates the AST (abstract syntax tree) which is tree like representation of the code. This tree helps the Js engine to understand the relationship between different parts of the code and the operations to perform.
Interpreted stage (Byte code generation)
In this stage, Js engine uses the AST to generate byte code (Bytecode is a lower-level, machine-independent format that's easier for the engine to optimize).
Profiling and Monitoring
As the code runs, Js engine profiles and monitors the performance. It tracks the hot code (piece of code that run slow or take time to process).
JIT (Just-in-Time) compilation
After Js engine detects hot code JIT kicks in to optimize performance of the code.
JIT converts byte code into optimized machine code for faster execution.
De-Optimization (Bail out)
If the assumptions made during the JIT optimization turn out to be incorrect then the Js engine deoptimized the code and removes JIT compiled code.
Garbage Collector
Throughout the execution process, Js engine runs a garbage collector to clean up the unused memory.
Execution Context & Event loop
When JavaScript code runs, Js engine creates an execution context.
Execution context
An execution context is an environment where code is executed and provides information about the variables, functions, and parameters defined in that environment.
There are two types of execution context
Global Execution Context, it is created when code execution starts.
Function Execution Context, it is created every time a function is invoked.
Call Stack
It is used to keep track of the execution contexts. When a script starts execution first the Global Execution Context is pushed in call stack after that Function Execution Context is pushed to it and pops out after completion
Event loop
Event loop enables JavaScript asynchronous, non-blocking behaviour despite being single threaded.
When an asynchronous code is encounter (eg. setTimeout), it is offered to Web APIs (where it stays until timer attach to it or till it’s completion) once the task is completed, it’s callback is pushed to the Task Queue.
Event loop continuously checks if the call stack is empty, if it is then it pushes task from Task Queue into call stack one by one.