How JavaScript Works in the Web Browser
Before you can really understand JavaScript, it helps to know where it actually lives and how it runs. Not just "it runs in the browser", but what's actually happening under the hood when your code executes
How the Browser is Structured
Each browser tab is completely independent, meaning that it gets its own JavaScript environment, main thread, event loop, and isolated V8 instance. That's why if one tab crashes, it doesn't bring down the rest, they don't share anything.
Inside that process, different components handle specific jobs:
- The Browser/UI layer manages the address bar, tabs, and everything you interact with visually in the browser itself
- The Network Stack handles HTTP requests, DNS lookups, and internet communication
- The Rendering Engine parses HTML, CSS, and JavaScript and turns them into visual content on screen
- Lower-level libraries help draw graphics using the GPU
- The JavaScript Engine (V8 in Chrome) is what actually executes your JavaScript code

How JavaScript Connects to the Page
The main purpose of JavaScript was to attach to DOM elements and listen to events. We can add JavaScript to a project by adding an inline <script> tag directly into the HTML, or we can have an external script file and import that.
When the browser encounters an external script tag, Blink (the rendering engine) spots the src attribute and fetches the file from the server. It sends a network request to download app.js, exactly the same way it would download an image or a CSS file. Once it arrives, it hands it off to V8 (the JavaScript Engine) to actually execute it.
So Blink is the one doing the fetching whilst V8 is the one doing the running.
JavaScript Execution Pipeline
Here's something a lot of developers don't fully understand — JavaScript is described as an interpreted language, but that's only half the story these days.
When you write Java, you run a compiler that takes your source code and produces a completely separate .class file that gets run by the Java Virtual Machine. As that file exists on its own, you could delete your original code and the compiled file would still work fine.
JavaScript doesn't work like that and there's no separate output file, no compilation step beforehand. This means that when you write a .js file, the browser picks it up and runs it directly as it goes.
But it's not purely interpreted either. Here's what actually happens inside V8 when your code arrives:
- If there are no errors, the internal V8 parser builds an Abstract Syntax Tree (AST).
- The AST gets handed to Ignition, V8's interpreter, which converts it into bytecode and starts executing it.
- If a function gets called frequently, Ignition flags it as hot and promotes it to TurboFan which is V8's optimising compiler which compiles it down to faster machine code on the fly.
That last part is what's called Just-In-Time (JIT) compilation. The code starts interpreted and gets compiled where it matters and it's why JavaScript is fast enough to power complex applications despite being a scripting language.
V8 actually has three levels of output depending on how 'hot' something is:
- Baseline — quick to produce, runs okay
- Sparkplug — a faster first pass, sits between Ignition and TurboFan
- TurboFan — fully optimised machine code for the paths that run constantly
Everything ultimately ends up as binary running on your CPU.

JavaScript is single threaded
Your JavaScript runs on one thread, one thing at a time, in order. There's no parallel execution happening inside your JS code. This sounds like a limitation (and it is), but it's also what makes JavaScript's behaviour predictable.
What are C++ bindings?
JavaScript can't talk to your hardware, file system, or network on its own. So when you write:
console.log('hello')
JavaScript itself has no idea how to print to a screen. What actually happens is V8 maps that console.log call to a pre-written C++ function that does know how to do it, as C++ is a lower-level language that can talk directly to the operating system.
So you're writing JavaScript, but under the hood it's constantly handing off the real work to C++ functions that were already compiled and ready to go. Those are also known as C++ bindings.
A simple way to think about it — you speak JavaScript, the computer speaks machine code, and C++ is the translator sitting in the middle doing the heavy lifting.

The JavaScript Execution Environment
When a tab opens, V8 creates a JavaScript execution environment. This is what gives your code access to everything it needs.
You get three things injected into your global scope:
- The document object — how you talk to the DOM. When you call document.getElementById(), it goes through a C++ binding, reads the DOM data structure inside the tab process, and hands the result back to you.
- The Browser Object Model (BOM) — how you talk to the browser itself. Cookies, local storage, navigation. Navigator and History live here.
- JavaScript built-ins — everything the language ships with. Array, Object, Date, Promise, fetch() and so on.
All of this lands on the window object, which is the global object in the browser. That's why you can write document instead of window.document — it's all sitting on window.
The call stack is what actually executes your code. Every function call gets pushed onto it and run. The event loop on the other hand, decides what and when things get pushed to the call stack.
The event loop continuously checks if the call stack is empty, and if it is, it pulls the next task waiting in the queue and pushes it onto the stack. That's how JavaScript handles things like setTimeout or fetch callbacks without blocking the execution context, but that's a deeper topic for another article.

The Abstract Syntax Tree and Scope
When V8 gets your JavaScript code, it's just a string of text. The engine can't run a string, it needs to understand the structure of it first. So it runs a parser that reads through your code and converts it into a tree structure called an Abstract Syntax Tree.
If you write:
const x = 5 + 3;
The parser doesn't see that as a line of text. It sees a variable declaration called x whose value is an addition expression between the number 5 and the number 3.
That structure, the relationships between all those pieces, is the AST. It's a map of your code that the engine can actually work with.

Scope defines where a variable can be accessed from. When you declare a variable inside a function, it only exists inside that function, you can't reach it from outside. When you declare it at the top level, it's available everywhere.
Scope gets built at the same time as the AST, before your code runs. The engine reads through your code and figures out exactly which variables belong to which parts of the program. This is called lexical scoping, meaning scope is determined by where you write your code, not by when or how it runs.
const name = 'Alice'; // global scope — accessible anywhere
function greet() {
const message = 'Hello'; // function scope — only accessible inside greet
console.log(message + ' ' + name); // can access both
}
greet(); // "Hello Alice"
console.log(message); // ReferenceError — message doesn't exist out here
Each function creates its own scope. If a variable isn't found in the current scope, JavaScript looks in the parent scope, then the parent's parent, all the way up to the global scope. That chain is called the scope chain.
Most developers write JavaScript every day without thinking about any of this. But knowing that V8 is parsing your code into a tree, that scope is figured out before anything runs, that every console.log is quietly handing off to C++ under the hood changes how you think about the language.
You don't need to memorise all of it, yet having the mental model makes you a better debugger, a better developer, and honestly makes JavaScript a lot less mysterious.
PS: This is based on my own research and understanding of how JavaScript works in the Web Browser. I'd always recommend double checking other resources if you want to go deeper. I wrote this mostly for myself, but if it helps someone else along the way, that's a win. Thanks for reading.