How JavaScript works
In this section, we will be talking about how JavaScript works. We will be talking about the JavaScript Engine, the Call Stack, and the Event Loop. Let's get started!
JavaScript Engine
A JavaScript Engine is the software responsible for executing JavaScript code. It parses, compiles, and executes JavaScript code efficiently.
JavaScript was originally designed to run in web browsers so every browser has a JavaScript Engine that executes JavaScript code. The JavaScript Engine is responsible for parsing and executing JavaScript code. The most popular JavaScript Engine is the V8 Engine which is used in Google Chrome and Node.js.
Other popular JavaScript Engines include:
- SpiderMonkey: Used in Mozilla Firefox
- JavaScriptCore: Used in Safari
- Chakra: Used in Microsoft Edge
The engine converts JavaScript code into machine code that the computer can understand. Here's how the JavaScript Engine works:
- Parsing: The JavaScript Engine parses the JavaScript code and converts it into an Abstract Syntax Tree (AST).
- Compilation: The JavaScript Engine compiles the AST into machine code and also optimizes the code for performance.
- Execution: The JavaScript Engine executes the machine code and produces the output.
Let's talk about the Execution context and why do you even need to know about it?
Execution Context
The Execution Context is a data structure that keeps track of the state of the program. It stores information about the variables, functions, and objects in the program. The Execution Context is created when a function is called and is destroyed when the function returns. There are two types of Execution Context:
- Global Execution Context: The Global Execution Context is created when the program starts. It contains information about the global scope, global variables, and global functions.
- Function Execution Context: The Function Execution Context is created when a function is called. It contains information about the local scope, local variables, and local functions.
Every JavaScript program has at least one Global Execution Context. When a function is called, a new Function Execution Context is created and added to the Call Stack. When the function returns, the Function Execution Context is removed from the Call Stack.
Components of the JavaScript Engine
JavaScript Engine consists of two main components:
- Memory Heap: This is where memory allocation happens. JavaScript handles all the garbage collection and that's one of the reasons why it is referred to as a high-level language.
- Call Stack: This is where the code is executed.
Memory Management
A good example that shows JavaScript's efficient use of memory and reduced risk of memory leaks is:
let user = {
name: "John",
age: 30
};
user = null;
When the user
object is set to null
, the object is no longer needed and can be removed from memory. JavaScript automatically handles the memory management and garbage collection.
Call Stack
The Call Stack is a data structure that records the function calls in a program. It keeps track of the function calls and the order in which they are called. When a function is called, it is added to the top of the Call Stack. When the function returns, it is removed from the Call Stack. It basically is used to keep track of and manage these execution contexts. Here is a simple example of how the Call Stack works:
function hello() {
console.log("Hello, World!");
}
hello();
When the hello
function is called, it is added to the Call Stack. When the function returns, it is removed from the Call Stack. The Call Stack is a Last In, First Out (LIFO) data structure. This means that the last function added to the Call Stack is the first function to be removed from the Call Stack.
Runtime Environment
The Runtime Environment is the environment in which the JavaScript code is executed. It consists of the JavaScript Engine, the Call Stack, and the Event Loop. The Runtime Environment is responsible for executing the JavaScript code and managing the execution context.
The JavaScript Engine executes the JavaScript code and produces the output. The Call Stack keeps track of the function calls in the program. The Event Loop allows JavaScript to perform non-blocking operations(we will talk about this later).
In a browser, the runtime includes:
- JavaScript Engine: The JavaScript Engine is responsible for executing JavaScript code.
- Web APIs: Web APIs are provided by the browser and allow JavaScript to interact with the browser environment. Examples of Web APIs include the DOM API, the Fetch API, and the setTimeout function.
- Event Loop: The Event Loop is a mechanism that allows JavaScript to perform non-blocking operations.
In Node.js, the runtime includes:
- JavaScript Engine: The JavaScript Engine is responsible for executing JavaScript code.
- Node.js APIs: Node.js APIs are provided by Node.js and allow JavaScript to interact with the Node.js environment. Examples of Node.js APIs include the fs module, the http module, and the setTimeout function.
- Event Loop: The Event Loop is a mechanism that allows JavaScript to perform non-blocking operations.
Event Loop
The Event Loop is a mechanism that allows JavaScript to perform non-blocking operations. JavaScript is a single-threaded language which means it can only execute one task at a time. This can be a problem when performing long-running tasks like fetching data from an API or reading a file from the disk. A good example of this is the setTimeout
function. The setTimeout
function is used to delay the execution of a function. Here is an example of how the setTimeout
function works:
console.log("Hello, World!");
setTimeout(() => {
console.log("Delayed Hello, World!");
}, 1000);
When the setTimeout
function is called, it is added to the Call Stack. The setTimeout
function is a Web API provided by the browser. The Web API waits for the specified time (in this case, 1000 milliseconds) and then adds the callback function to the Task Queue. The Event Loop checks the Task Queue and moves the callback function to the Call Stack when the Call Stack is empty.
Key components of Execution
Hoisting
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their containing scope during the compilation phase. This means that you can use a variable or function before it is declared. Here is an example of how hoisting works:
console.log(name);
var name = "John";
"Hoising" meants to raise or lift something up. In JavaScript, variables declared with the var
keyword are hoisted to the top of their containing scope but with an initial value of undefined
. We will talk about the let
and const
keywords later or even scope in general in the later sections of this guide.
When the code is executed, the JavaScript Engine hoists the variable declaration to the top of the scope. This means that the code is interpreted as:
var name;
console.log(name);
name = "John";
Scopes
Defines the accessibility of variables:
- Global Scope: Accessible anywhere.
- Local Scope: Accessible within a function or block.
Closures
A closure is a function that has access to its own scope and the scope of its parent function. This means that a closure can access variables and functions defined in its own scope and the scope of its parent function. Here is an example of how closures work:
function outer() {
let name = "John";
function inner() {
console.log(name);
}
return inner;
}
let innerFunction = outer();
innerFunction();
When the inner
function is called, it has access to the name
variable defined in the outer
function. This is because the inner
function is a closure and has access to its own scope and the scope of its parent function.
Nice! You have learned about the JavaScript Engine, the Execution Context, the Call Stack, and the Event Loop. You are now ready to write, run, and debug JavaScript code in the browser. We keep going 🚀
Made with ❤️ by Fasakin Henry