How JavaScript Really Works: Unraveling the Magic Behind the Web

Eftee Codes
5 min readOct 23, 2024

--

JavaScript (JS) is the backbone of modern web applications, bringing life to static HTML and CSS. Whether you’re a beginner or an experienced developer, knowing how JavaScript works under the hood is essential. In this blog, we’ll dive deep into JavaScript’s mechanics — its engines, event loop, memory management, and more. By the end, you’ll not only understand how JS works but also why it’s so powerful.

What Is JavaScript?

JavaScript is a high-level, interpreted, and multi-paradigm programming language primarily used for web development. It’s a language that can both manipulate DOM elements in the browser and handle server-side operations when running on Node.js.

  • High-level: Abstracts away many complexities of lower-level languages like C or Java.
  • Interpreted: JS code is executed line-by-line by the browser or runtime, instead of being pre-compiled.
  • Multi-paradigm: Supports object-oriented, functional, and procedural programming.

JavaScript Engines: V8 and Beyond

JavaScript doesn’t run in isolation. It requires an engine to interpret and execute the code. The most popular JavaScript engine is V8, developed by Google. It powers both Google Chrome and Node.js. Other engines include SpiderMonkey (used in Firefox) and JavaScriptCore (used in Safari).

How Does V8 Work?

V8 compiles JavaScript to machine code at runtime, making it extremely fast. Here’s the process:

  1. Parsing: The JavaScript code is first converted into an Abstract Syntax Tree (AST).
  2. Bytecode Generation: The AST is translated into intermediate bytecode.
  3. Just-In-Time (JIT) Compilation: Hot code paths (frequently executed code) are optimized into machine code to improve performance.

Todo: Try this! Open Chrome’s Developer Tools, go to the “Sources” tab, and write a small JavaScript snippet like console.log('Hello World!'). Run it, and observe how the JS engine works in real time!

The JavaScript Runtime: Browser and Node.js

When we talk about JavaScript runtime, we mean the environment in which JS code runs. This could either be the browser (client-side) or Node.js (server-side). Both environments differ in their capabilities but share a common engine.

  • Browser: It provides APIs like the DOM, Web Storage, and Fetch.
  • Node.js: Here, JavaScript can interact with file systems, databases, and network requests through built-in modules.

The Event Loop: Heart of Asynchronous JS

JavaScript is single-threaded, meaning it can only perform one task at a time. So how does it handle multiple tasks like HTTP requests, timers, and user input? The answer lies in the Event Loop.

How the Event Loop Works

  1. Call Stack: When you run JavaScript code, the commands are added to the Call Stack (LIFO structure).
  2. Web APIs: When JavaScript code encounters an asynchronous task (like setTimeout or a network request), the browser handles it outside the Call Stack via Web APIs.
  3. Task Queue: Once the async task completes, the callback is moved to the Task Queue.
  4. Event Loop: The Event Loop constantly checks if the Call Stack is empty. If it is, it moves tasks from the Task Queue into the Call Stack.
console.log('Start');

setTimeout(() => {
console.log('This runs after 2 seconds');
}, 2000);

console.log('End');

In the example above, setTimeout is asynchronous, so while the timer is counting, the Event Loop lets console.log('End') execute. After 2 seconds, the setTimeout callback is pushed to the Call Stack.

Todo: You can visualize the Event Loop in action using Loupe. It’s an interactive tool that shows how JavaScript executes code step-by-step!

JavaScript Call Stack: How It Executes Code

The Call Stack is where JavaScript keeps track of function calls. Think of it as a stack of books — whenever a function is invoked, it’s added to the top. When the function completes, it’s removed from the stack.

Call Stack in Action:

function first() {
second();
console.log('First function');
}

function second() {
console.log('Second function');
}

first();

Here’s the sequence of events:

  1. The first() function is added to the Call Stack.
  2. Inside first(), the second() function is called, and it's added to the Call Stack.
  3. Once second() finishes, it’s popped off the stack, and the control goes back to first(), which then completes and is removed.

Memory Management: Garbage Collection

JavaScript automatically manages memory allocation and deallocation using a process called Garbage Collection. The most common strategy is mark-and-sweep, where unreachable objects (those no longer referenced) are periodically cleaned up by the engine.

Memory Leaks

Poor memory management can lead to memory leaks. Examples include:

  • Global Variables: If not properly scoped, they persist throughout the lifecycle of the app.
  • Event Listeners: Forgetting to remove unused listeners can cause memory buildup.

👨‍💻 Tip: Use Chrome DevTools to monitor memory usage under the “Memory” tab. Keeping an eye on memory profiles helps ensure your app stays efficient!

Understanding Hoisting and Scope

Hoisting allows JavaScript to move declarations to the top of the scope before code execution. This applies to variables and functions, but not the assignments.

console.log(x); // undefined
var x = 5;

In the above example, var x is hoisted, but its assignment x = 5 is not. That’s why the first console.log outputs undefined.

Closures: Mastering JavaScript Functions

A closure is a feature in JavaScript where an inner function has access to variables from its outer function, even after the outer function has finished executing.

function outer() {
let counter = 0;
return function inner() {
counter++;
return counter;
};
}

const increment = outer();
console.log(increment()); // 1
console.log(increment()); // 2

Closures are powerful for creating private variables and functions that remember the state.

How JavaScript Executes Asynchronous Code

JavaScript handles async operations via:

  • Callbacks: Old-school method, can lead to “callback hell.”
  • Promises: More readable and chainable.
  • Async/Await: Cleaner syntax for handling promises in a synchronous-looking manner.
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}

fetchData();

Conclusion: JavaScript Powers the Web

JavaScript’s engine, event loop, and memory management systems make it a robust tool for modern web development. Its versatility allows it to be used for both frontend and backend development, and understanding how it works under the hood is key to becoming an effective developer.

Now that you have a better grasp of how JavaScript works, you’re ready to tackle more advanced concepts and write optimized, efficient code!

Feel free to leave your comments or ask questions! I’d love to discuss more on JavaScript’s internals.

Happy Coding!

--

--

Eftee Codes
Eftee Codes

Written by Eftee Codes

Full Stack Developer | Tech Enthusiast | Psychology & Economics Aficionado | Turning Code into Fun Experiences 🎮🧠

No responses yet