How JavaScript Runs

Before you write a line, know what actually runs it: the engine, the host, and how scripts load.

JavaScript5 min readConcept 1 of 62

An engine turns your source into running code

JavaScript is a high-level language that runs inside an engine: V8 in Chrome and Node, SpiderMonkey in Firefox, JavaScriptCore in Safari. You never run JS directly on the CPU; the engine does it for you.

The engine first parses your source text into a tree that represents its structure (an Abstract Syntax Tree). It then compiles that tree to bytecode and starts running it right away. This is why people say JS is just-in-time compiled, not purely interpreted.

While your code runs, the engine watches which functions run often (hot code) and recompiles those to optimized machine code for speed. If an assumption breaks, for example a variable that was always a number suddenly holds a string, it quietly falls back to the slower path.

The language is small; the host gives it powers

The JavaScript language itself (the ECMAScript standard) defines syntax, types, functions, and built-ins like Array and Math. It does not know what a web page or a file is.

Those abilities come from the host environment. In a browser you get document, fetch, setTimeout, and localStorage. In Node you get fs, process, and require. Same language, different powers.

Knowing this split saves real confusion: fetch failing in an old Node version is not a JavaScript problem, it is a host that did not provide that API. The language and its surroundings are separate layers.

Loading a script: default, defer, async, module

A plain <script src="..."> pauses HTML parsing while it downloads and runs. Put many at the top of <head> and the page stalls before anything paints.

<script defer> downloads in the background and runs after the HTML is fully parsed, in document order. This is the safe default for scripts that touch the page.

<script async> downloads in the background and runs the instant it is ready, which may be before or after parsing finishes, and in no guaranteed order. Use it for independent scripts like analytics.

<script type="module"> is deferred by default, runs in strict mode automatically, and gets its own module scope so top-level variables do not leak to the global object. This is the modern default.

Try it

Follow one line of source as it moves through the engine, and see which layer owns each piece.

// your source
const total = nums.map(
n => n * 2
);
// the engine takes it from here

Inside the engine

  1. 1Parse: source text to an Abstract Syntax Tree (AST)
  2. 2Compile: AST to bytecode, starts running right away
  3. 3Optimize (JIT): hot functions recompiled to fast machine code
  4. 4Run: the engine executes, deoptimizing if assumptions break

The language (ECMAScript)

  • Array
  • Math
  • typeof
  • Promise
  • JSON

The host (browser / Node)

  • document
  • fetch
  • setTimeout
  • localStorage

One line of source becomes an AST, then bytecode, then optimized machine code. The engine runs the language; the host adds the extras.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1Which of these is provided by the host environment, not the JavaScript language itself?
  2. 2You want a script that touches the page to run after the HTML is parsed, in order. Which attribute?
  3. 3What does a JIT (just-in-time) compiler do that a pure interpreter does not?

Remember this

  • An engine (V8, SpiderMonkey, JavaScriptCore) parses your source to an AST, then JIT-compiles and runs it.
  • The language is ECMAScript; document, fetch, setTimeout come from the host (browser or Node), not the language.
  • defer runs after parsing in order; async runs as soon as it loads; type="module" is deferred and strict by default.
  • Execution is not always source order: hoisting and deferred scripts change when things run.

Done with this concept?

Mark it complete to track your progress. No login needed.