373 lines
15 KiB
Markdown
373 lines
15 KiB
Markdown
# 02. JavaScript in the Browser
|
|
|
|
The previous chapter explained JavaScript as a language. This chapter explains the environment that makes JavaScript useful on the web.
|
|
|
|
That distinction matters more than many candidates realize. The language gives you functions, objects, promises, modules, and classes. The browser gives you a page, a DOM, timers, rendering, networking, storage, security boundaries, and user events. Most frontend engineering work happens at the boundary between those two layers.
|
|
|
|
If Chapter 1 answered, "How does JavaScript think?" this chapter answers, "Where does browser JavaScript actually live, and who gives it its powers?"
|
|
|
|
This file connects naturally to:
|
|
|
|
- [01-javascript-fundamentals.md](./01-javascript-fundamentals.md), which explains the execution model the browser embeds
|
|
- [03-dom-event-loop-rendering.md](./03-dom-event-loop-rendering.md), which dives into event loop and rendering details
|
|
- [04-networking-storage-security.md](./04-networking-storage-security.md), which explains how browser-provided capabilities are constrained by security rules
|
|
|
|
## The Browser Is Not Just a Window for JavaScript
|
|
|
|
When people casually say, "JavaScript runs in the browser," they often picture a single machine that simply reads code and executes it. Real browsers are more like miniature operating systems dedicated to documents and apps.
|
|
|
|
They have to coordinate:
|
|
|
|
- HTML parsing
|
|
- CSS parsing and style resolution
|
|
- JavaScript execution
|
|
- network requests
|
|
- image and font decoding
|
|
- input events
|
|
- layout, painting, and compositing
|
|
- sandboxing and security isolation
|
|
- storage and caching
|
|
|
|
So a browser runtime is not "JavaScript plus some extra APIs." It is a complex host that embeds a JavaScript engine into a much larger document and rendering system.
|
|
|
|
## JavaScript Engine vs Browser Environment
|
|
|
|
This is the most important conceptual split in browser JavaScript.
|
|
|
|
### The JavaScript Engine
|
|
|
|
The engine is responsible for the language itself:
|
|
|
|
- parsing source code
|
|
- producing internal representations such as syntax trees and bytecode
|
|
- managing execution contexts and the call stack
|
|
- allocating objects and garbage collecting memory
|
|
- optimizing hot code paths
|
|
|
|
Examples:
|
|
|
|
- Chrome and Edge use V8
|
|
- Firefox uses SpiderMonkey
|
|
- Safari uses JavaScriptCore
|
|
|
|
If an interview asks about Chrome specifically, mentioning V8 is useful. If the discussion is broader, focus on the engine role rather than vendor trivia.
|
|
|
|
### The Browser Environment
|
|
|
|
The browser environment is responsible for everything that makes web applications interactive:
|
|
|
|
- the DOM API
|
|
- timers like `setTimeout`
|
|
- networking through `fetch`, `XMLHttpRequest`, WebSocket, and more
|
|
- event handling for clicks, keyboard input, scrolling, and navigation
|
|
- rendering pipeline integration
|
|
- storage such as cookies, `localStorage`, `sessionStorage`, and IndexedDB
|
|
|
|
These APIs are not part of ECMAScript itself. They are host-provided capabilities.
|
|
|
|
### A Strong Interview Answer
|
|
|
|
If asked, "Is `fetch` part of JavaScript?" the best short answer is:
|
|
|
|
"No. `fetch` is not part of the ECMAScript language specification. It is provided by the host environment, such as a browser or modern Node.js runtime."
|
|
|
|
That answer shows you understand the layering.
|
|
|
|
## High-Level Browser Architecture
|
|
|
|
Real browsers are multi-process and heavily optimized, but for frontend reasoning a high-level model is enough.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
A[HTML CSS JS assets] --> B[Browser process]
|
|
B --> C[Renderer process for tab]
|
|
C --> D[HTML parser]
|
|
C --> E[Style engine]
|
|
C --> F[JavaScript engine]
|
|
C --> G[DOM and render data]
|
|
C --> H[Event system]
|
|
C --> I[Scheduler]
|
|
B --> J[Network stack]
|
|
B --> K[Storage and cache]
|
|
G --> L[Layout paint composite]
|
|
H --> F
|
|
F --> G
|
|
J --> C
|
|
K --> C
|
|
```
|
|
|
|
This diagram hides many details, but it captures the engineering truth that matters most: JavaScript is only one subsystem in a browser page.
|
|
|
|
## Chrome, V8, and Blink at a Useful Level
|
|
|
|
Interview preparation often suffers from too much shallow browser jargon. You do not need vendor-specific internals for every interview, but you do need a credible mental model.
|
|
|
|
### V8
|
|
|
|
V8 is Chrome's JavaScript engine. At a high level it:
|
|
|
|
- parses JavaScript source
|
|
- generates bytecode
|
|
- runs code through an interpreter first
|
|
- optimizes hot code paths with a compiler
|
|
- performs garbage collection
|
|
|
|
The broad performance idea is simple: browsers do not want to spend too long optimizing cold code that runs once, but they also do not want hot UI logic to remain slow forever. So they often start quickly and optimize as usage patterns become clear.
|
|
|
|
### Blink
|
|
|
|
Blink is Chrome's rendering engine. At a useful high level it handles:
|
|
|
|
- parsing HTML into the DOM
|
|
- parsing CSS into style structures
|
|
- calculating layout
|
|
- painting pixels
|
|
- coordinating rendering updates with the rest of the browser
|
|
|
|
From a frontend engineer's point of view, a lot of performance work is really about respecting how the rendering engine wants to work. If you force layout repeatedly, mutate the DOM too often, or block the main thread, you are not just writing "slow JavaScript." You are fighting Blink's scheduling and rendering pipeline.
|
|
|
|
### WebKit Concepts at a High Level
|
|
|
|
Safari uses JavaScriptCore as its engine and WebKit as its broader engine stack. Even if you mostly target Chromium browsers, it is worth understanding that there is no single universal implementation. Standards define behavior, but engine teams make different tradeoffs in optimization, scheduling, and edge-case behavior.
|
|
|
|
That is one reason real-world engineering must care about standards and cross-browser testing rather than assuming that Chrome behavior alone defines the web.
|
|
|
|
## How JavaScript Gets Embedded Into a Page
|
|
|
|
The browser does not magically know when or how to execute your script. Script loading is part of document parsing and page scheduling.
|
|
|
|
### Classic Script Tags
|
|
|
|
```html
|
|
<script src="app.js"></script>
|
|
```
|
|
|
|
With a classic script tag in the document body or head, the browser typically:
|
|
|
|
1. parses HTML until it encounters the script
|
|
2. pauses parsing
|
|
3. fetches the script if needed
|
|
4. executes the script
|
|
5. resumes HTML parsing
|
|
|
|
That parser-blocking behavior is one reason careless script placement can slow page startup.
|
|
|
|
### `defer`
|
|
|
|
```html
|
|
<script defer src="app.js"></script>
|
|
```
|
|
|
|
With `defer`, the browser can continue parsing HTML while the script is fetched, and execution happens after the document has been parsed, before `DOMContentLoaded` fires.
|
|
|
|
`defer` is usually what you want for page-level scripts that depend on the DOM being present.
|
|
|
|
### `async`
|
|
|
|
```html
|
|
<script async src="analytics.js"></script>
|
|
```
|
|
|
|
With `async`, the browser fetches in parallel and executes as soon as the script is ready, independent of document parsing order. That makes it good for independent scripts, but dangerous for scripts that depend on other scripts or on predictable execution order.
|
|
|
|
### ES Modules
|
|
|
|
```html
|
|
<script type="module" src="main.js"></script>
|
|
```
|
|
|
|
Module scripts behave more like deferred scripts by default and add better scoping and import/export semantics. They avoid many historical problems of script globals colliding with one another.
|
|
|
|
### Script Loading Timeline
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Parser as HTML Parser
|
|
participant Network as Network
|
|
participant Engine as JS Engine
|
|
|
|
Parser->>Parser: Parse HTML
|
|
Parser->>Network: Discover script URL
|
|
alt classic script
|
|
Parser->>Parser: Pause parsing
|
|
Network-->>Parser: Script bytes ready
|
|
Parser->>Engine: Execute now
|
|
Engine-->>Parser: Done
|
|
Parser->>Parser: Resume parsing
|
|
else defer or module
|
|
Parser->>Parser: Continue parsing
|
|
Network-->>Parser: Script bytes ready
|
|
Parser->>Engine: Execute after parse completes
|
|
else async
|
|
Parser->>Parser: Continue parsing until ready
|
|
Network-->>Parser: Script bytes ready
|
|
Parser->>Engine: Execute immediately when available
|
|
end
|
|
```
|
|
|
|
### Why the Browser Works This Way
|
|
|
|
The browser wants to preserve correctness first and optimize second.
|
|
|
|
- Classic scripts were designed in an era where order and shared globals were the normal pattern.
|
|
- `defer` lets the browser keep parsing without breaking predictable order.
|
|
- `async` prioritizes early independent execution when order does not matter.
|
|
- modules modernize the system with better dependency management and isolation.
|
|
|
|
These are tradeoffs between compatibility, performance, and predictability.
|
|
|
|
## Web APIs: The Browser's Power Layer
|
|
|
|
Once the script is running, it can call APIs that the engine alone does not provide.
|
|
|
|
### DOM APIs
|
|
|
|
These let JavaScript inspect and change the document:
|
|
|
|
- `document.querySelector`
|
|
- `element.append`
|
|
- `element.classList.add`
|
|
- `addEventListener`
|
|
|
|
DOM APIs connect JavaScript to the visible page. Without them, JavaScript in the browser would still be a language, but it would not be able to build a UI.
|
|
|
|
### Timer APIs
|
|
|
|
`setTimeout` and `setInterval` are browser scheduling tools, not language keywords. They register work with the host environment, which later re-enters JavaScript when the scheduled time has elapsed and the event loop allows it.
|
|
|
|
Important nuance: `setTimeout(fn, 0)` does not mean "run immediately." It means "schedule this as a future task after the current synchronous work and any higher-priority scheduled work have had their turn."
|
|
|
|
### Networking APIs
|
|
|
|
`fetch` lets JavaScript request resources without forcing full-page navigation.
|
|
|
|
This capability is what made rich single-page applications practical. Before asynchronous in-page data fetching, much of web interaction meant whole-page reloads.
|
|
|
|
### Observer and Browser Integration APIs
|
|
|
|
Modern browsers expose APIs that let code react efficiently to browser-managed events:
|
|
|
|
- `requestAnimationFrame`
|
|
- `IntersectionObserver`
|
|
- `ResizeObserver`
|
|
- `MutationObserver`
|
|
- `AbortController`
|
|
|
|
These exist because the browser knows things your code cannot infer efficiently on its own. Instead of polling, you often get better correctness and performance by letting the browser notify you.
|
|
|
|
## The Runtime Model at a High Level
|
|
|
|
The detailed event loop comes in Chapter 3, but you should already hold the high-level picture.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
A[Your JavaScript] --> B[Call stack]
|
|
B --> C[Browser Web APIs]
|
|
C --> D[Task queue]
|
|
C --> E[Microtask queue triggers]
|
|
D --> B
|
|
E --> B
|
|
B --> F[DOM updates]
|
|
F --> G[Render pipeline]
|
|
```
|
|
|
|
The important part is not memorizing arrows. The important part is understanding responsibility boundaries:
|
|
|
|
- the engine executes JavaScript on the call stack
|
|
- the browser owns host features like timers, networking, and DOM event sources
|
|
- the browser decides when queued callbacks are allowed back onto the stack
|
|
- rendering is coordinated with this scheduling model rather than happening after every line of code
|
|
|
|
This is why frontend debugging often requires reasoning across layers, not just staring at JavaScript syntax.
|
|
|
|
## The Global Object in Browsers
|
|
|
|
In browser scripts, the global object is historically `window`. Many browser globals live there:
|
|
|
|
- `window.document`
|
|
- `window.setTimeout`
|
|
- `window.fetch`
|
|
- `window.localStorage`
|
|
|
|
In global script code, `var` declarations historically create properties on `window`, which is one reason legacy browser JavaScript was prone to name collisions.
|
|
|
|
ES modules improve this story because top-level declarations in modules do not leak the same way into the global object.
|
|
|
|
## Browser JavaScript vs Node.js
|
|
|
|
Interviewers often use this comparison to test whether you understand what belongs to the language and what belongs to the host.
|
|
|
|
| Topic | Browser | Node.js |
|
|
| --- | --- | --- |
|
|
| Main purpose | UI, documents, user interaction | Servers, tooling, scripts |
|
|
| DOM access | Yes | No |
|
|
| File system access | No direct user-file access by default | Yes |
|
|
| Networking focus | Page requests, subresources, APIs, sockets | Servers, APIs, CLI tools, sockets |
|
|
| Global object | `window` or `self` depending on context | `global` |
|
|
| Event loop flavor | Oriented around page, rendering, input | Oriented around I/O and server tasks |
|
|
| Rendering pipeline | Central concern | Usually none |
|
|
|
|
### Same Language, Different Constraints
|
|
|
|
Both environments run JavaScript, but they optimize for different jobs.
|
|
|
|
The browser is security-sensitive and user-facing:
|
|
|
|
- pages from different origins must be isolated
|
|
- direct disk access is restricted
|
|
- rendering smoothness matters
|
|
- input latency matters
|
|
|
|
Node.js is server- and tooling-oriented:
|
|
|
|
- file system access is normal
|
|
- network server APIs are normal
|
|
- there is no DOM
|
|
- rendering is not part of the runtime model
|
|
|
|
So when a candidate says, "JavaScript can do X," a strong interviewer may immediately ask, "In which environment?"
|
|
|
|
## Web Workers: A Useful Boundary Case
|
|
|
|
Browser JavaScript is often described as single-threaded, but that statement needs precision.
|
|
|
|
The main page's synchronous JavaScript execution happens on a single call stack on the main thread. But browsers also provide Web Workers, which let you run JavaScript in separate worker contexts without direct DOM access.
|
|
|
|
That tells you something fundamental about browser design:
|
|
|
|
- DOM and rendering stay centralized and carefully controlled
|
|
- CPU-heavy work can be moved away from the main thread when needed
|
|
- communication happens through messages, not shared direct access by default
|
|
|
|
That model protects rendering consistency and reduces a whole class of race conditions in UI code.
|
|
|
|
## How Real Applications Use the Browser Runtime
|
|
|
|
A modern React or Vue app is still just browser JavaScript with a structured architecture on top.
|
|
|
|
When the app starts, it typically:
|
|
|
|
1. loads script bundles or modules
|
|
2. creates application state
|
|
3. attaches event listeners
|
|
4. reads routing information from the URL
|
|
5. fetches data from APIs
|
|
6. updates the DOM, often through a framework abstraction
|
|
7. continues reacting to user events and network responses
|
|
|
|
Nothing about a framework escapes the browser runtime. It only organizes it.
|
|
|
|
That is why framework expertise is much more durable when it is built on browser fundamentals. If you know where the event loop, DOM, rendering pipeline, and network layer sit, you can reason about almost any frontend stack.
|
|
|
|
## Interview-Ready Summary
|
|
|
|
- The JavaScript engine handles the language: parsing, execution, memory management, and optimization.
|
|
- The browser environment provides host APIs such as the DOM, timers, networking, storage, and events.
|
|
- `fetch`, `setTimeout`, and DOM methods are browser-provided APIs, not core ECMAScript language features.
|
|
- Script loading strategy matters because classic scripts can block parsing, while `defer`, `async`, and modules make different performance and ordering tradeoffs.
|
|
- In Chrome, V8 handles JavaScript execution while Blink handles document and rendering behavior at a high level.
|
|
- Browser JavaScript and Node.js share the language but run inside different host environments with different capabilities and constraints.
|
|
|
|
## What to Read Next
|
|
|
|
Continue with [03-dom-event-loop-rendering.md](./03-dom-event-loop-rendering.md). That chapter explains how browser documents are represented, how callbacks are actually scheduled, and why DOM changes and rendering cost what they cost. |