JavaScript — Reference

Source: https://tc39.es/ecma262/

JavaScript

  • Created: 1995 by Brendan Eich at Netscape (originally “Mocha”, then “LiveScript”); standardized as ECMAScript by Ecma International TC39
  • Latest stable: ECMAScript 2025 ratified mid-2025; ES2026 in flight; latest editor’s draft tracks https://tc39.es/ecma262/. Node.js 26 (Current, 2026-05-05), Node.js 24 LTS “Krypton” (2025-05-06)
  • Paradigms: multi-paradigm — prototype-based OO, functional, imperative, event-driven; classes are syntactic sugar over prototypes
  • Typing: dynamic, weakly typed (with implicit coercion). Static layer via TypeScript or JSDoc.
  • Memory: garbage collected — generational, mark-sweep + scavenger; per-engine details (V8, SpiderMonkey, JavaScriptCore)
  • Compilation: JIT-compiled in modern engines (V8 Ignition+TurboFan/Maglev, SpiderMonkey IonMonkey/WarpMonkey, JSC LLInt+Baseline+DFG+FTL). Originally interpreted.
  • Primary domains: browser frontend, server-side (Node.js, Deno, Bun), desktop (Electron, Tauri), mobile (React Native, Capacitor), serverless / edge, embedded scripting
  • Official docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript

At a glance

  • Standard: ECMA-262, annual yearly snapshots since ES2015. Living spec at https://tc39.es/ecma262/.
  • Engines: V8 (Chrome, Node.js, Deno, Edge), SpiderMonkey (Firefox), JavaScriptCore / Nitro (Safari, Bun uses JSC), Hermes (React Native), QuickJS (embedded).
  • Runtimes: browsers, Node.js, Deno (TS-first, secure-by-default), Bun (Zig + JSC, npm-compatible, fast), Cloudflare Workers (V8 isolates), edge platforms (Vercel, Netlify, Fastly Compute).
  • Governance: TC39 with stage process 0-4; Stage 4 = in the next yearly snapshot.

Getting started

Install:

  • Browsers: built in.
  • Node.js: https://nodejs.org/ — installer, or version manager: nvm (POSIX), fnm (Rust), volta, nvs (Windows). Bun and Deno include their own version managers (bun upgrade, deno upgrade).
  • For Node 24+ projects, prefer Corepack to pin pnpm/yarn versions.

Hello world (browser):

<script>console.log("Hello, world!");</script>

Hello world (Node.js):

// hello.mjs
console.log("Hello, world!");

Run: node hello.mjs.

Project layout (Node, ESM):

myapp/
  package.json          # "type": "module"
  src/
    index.js
  test/
    index.test.js
  node_modules/
  package-lock.json

Package managers:

  • npm (bundled with Node), pnpm (content-addressed store, fast), yarn (Berry / v4, PnP), bun (built-in).
  • Lockfiles: package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb.

REPL / playground: node or node --experimental-repl-await. Browser DevTools console. Online: https://playcode.io, https://stackblitz.com, MDN’s “Try it” boxes.

Basics

Primitives: number (IEEE 754 double, 53-bit safe int), bigint (123n), string (UTF-16), boolean, symbol, undefined, null. Plus object (incl. arrays, functions, dates, maps).

Variables / scope:

const x = 10;       // block-scoped, immutable binding (value can mutate if object)
let y = 1;          // block-scoped, reassignable
var z = 2;          // function-scoped, hoisted (legacy — avoid)

Closures capture by reference; this is dynamic (fn.call, arrow functions inherit lexical this).

Control flow: if/else, for, for...of (iterables), for...in (enumerable keys, avoid for arrays), while, do/while, switch, try/catch/finally, throw.

Functions:

function add(a, b = 0, ...rest) { return a + b + rest.length; }
const sub = (a, b) => a - b;       // arrow — no own `this`, no `arguments`
async function fetchJson(url) { return (await fetch(url)).json(); }
function* range(n) { for (let i = 0; i < n; i++) yield i; }  // generator

Strings: template literals (backtick) with ${expr}, tagged templates, raw strings via String.raw.

const name = "world";
console.log(`Hello, ${name}!`);

Built-in collections: Array, Object (string-keyed), Map, Set, WeakMap, WeakSet, WeakRef, Date, RegExp, typed arrays (Uint8Array, Float32Array, etc.), ArrayBuffer, SharedArrayBuffer, DataView.

Intermediate

Types & inference: none natively. JSDoc + // @ts-check gives editor-grade typing without TS. Most modern Node/Deno projects use TypeScript directly. Type erasure proposal (Stage 1) would let JS engines parse-and-strip TS-style annotations.

Modules: ESM (import/export, .mjs or "type":"module") is the standard. CommonJS (require/module.exports) is legacy but pervasive in Node. Dynamic import() returns a Promise. Import attributes for non-JS resources: import data from "./d.json" with { type: "json" }.

Errors: throw any value, but throw Error subclasses (TypeError, RangeError, SyntaxError, custom class MyError extends Error). try/catch (err)err is unknown style; check with instanceof. AggregateError for Promise.any. Error.cause for chaining.

Concurrency:

  • Single-threaded event loop per realm. Tasks vs microtasks (queueMicrotask).
  • Promise (.then / await / Promise.all / Promise.allSettled / Promise.race / Promise.any / Promise.try).
  • Async iterators (for await ... of) and async generators.
  • Workers: Worker (browsers), worker_threads (Node), Web Worker / Service Worker, SharedWorker.
  • SharedArrayBuffer + Atomics for shared-memory parallelism.
  • AbortController / AbortSignal for cancellation.

File I/O / networking:

  • Browser: fetch, WebSocket, XMLHttpRequest (legacy), IndexedDB, OPFS.
  • Node: node:fs/promises, node:fs, node:net, node:http, node:https, node:dgram, plus a built-in fetch since 18, node:test runner, node:sqlite (24+).
  • Deno/Bun: web-standard APIs first (fetch, Request, Response).

Stdlib highlights: Intl (i18n: Intl.NumberFormat, Intl.DateTimeFormat, Intl.Segmenter, Intl.Collator), URL / URLPattern, TextEncoder/TextDecoder, crypto.subtle (Web Crypto), structuredClone, Temporal (Stage 3, shipping in engines).

Advanced

Memory model & GC:

  • V8: generational (young / old), Scavenger for young, Mark-Compact for old, Orinoco concurrent / parallel collector. Pointer compression on 64-bit.
  • Tune Node: --max-old-space-size=4096, --max-semi-space-size, --gc-interval, --expose-gc for global.gc().
  • Inspect heap: --inspect, Chrome DevTools Heap Snapshot, clinic.js, 0x (flamegraphs).

Concurrency deep dive:

  • Microtask queue (Promises) drains between macrotasks.
  • Atomics.wait/notify for futex-style sync over SharedArrayBuffer.
  • Worker pools via piscina (Node).
  • AsyncContext (Stage 3 TC39) — async-aware context propagation.
  • Node node:cluster for multi-process; worker_threads for CPU-bound.

FFI / interop:

  • Node: N-API / node-addon-api (stable C ABI), Node-API with napi-rs (Rust), neon (Rust), legacy nan. WASI for portable native modules.
  • Deno: Deno.dlopen direct FFI to .so/.dll/.dylib.
  • Bun: bun:ffi direct FFI.
  • Browser: WebAssembly (WebAssembly.instantiate), Emscripten, wasm-bindgen (Rust).

Reflection / introspection: Object.keys/getOwnPropertyDescriptors/getPrototypeOf/getOwnPropertyNames, Reflect.*, Symbol.iterator/asyncIterator/hasInstance/toPrimitive, instanceof, typeof, Function.prototype.toString returns source.

Performance tuning:

  • V8 inspection: --trace-opt, --trace-deopt, --prof, --prof-process, --print-bytecode, --allow-natives-syntax then %OptimizeFunctionOnNextCall(fn).
  • Tools: Chrome DevTools Performance / Memory, node --inspect, clinic.js doctor/flame/bubbleprof, 0x, autocannon (HTTP load), mitata / tinybench (microbenchmarks).
  • Avoid hidden-class polymorphism — initialize objects with the same property order; don’t add/delete props on hot paths.

God mode

Proxy & Reflect: intercept any object operation.

const audited = new Proxy(target, {
  get(t, p, r) { console.log("get", p); return Reflect.get(t, p, r); },
  set(t, p, v, r) { console.log("set", p, v); return Reflect.set(t, p, v, r); },
});

Underpins Vue 3 reactivity, immer, MobX, observable proxies.

Iteration & generator protocols: any object implementing [Symbol.iterator]() or [Symbol.asyncIterator]() works in for...of. Generators support .return() / .throw() for cooperative cancellation.

V8 internals: hidden classes (Maps), inline caches (ICs), CodeStubAssembler / Torque for builtins, Sea-of-Nodes IR. --print-code dumps machine code for hot functions. Sparkplug → Maglev → TurboFan tier-up.

AsyncContext: propagate context across await boundaries without monkeypatching. Replaces domain / async-local-storage hacks.

SharedArrayBuffer + Atomics: the only path to true shared-memory parallelism. Requires COOP/COEP cross-origin isolation in browsers. Atomics.waitAsync is non-blocking.

BigInt + Atomics: BigInt64Array works with Atomics; useful for lock-free 64-bit counters and futexes from WASM.

Eval scope tricks: direct eval("x = 1") sees / mutates caller’s local scope; indirect (0, eval)("x = 1") runs in global scope. new Function(...) always global. Both are usually the wrong answer.

Realms / iframes: different realms have different Array, so arr instanceof Array can lie. Use Array.isArray(arr).

Decorators (Stage 3, ES2023+ shipping):

function logged(value, { kind, name }) {
  if (kind === "method") return function (...args) { console.log(name); return value.apply(this, args); };
}
class C { @logged greet() {} }

Engine embedding: V8 embedder API (v8::Isolate, v8::Context), JSC API (JSContext, JSValue), QuickJS for tiny embeds, Hermes for AOT-compiled bytecode (React Native).

Bytecode:

  • V8 Ignition: stack-based, viewable via --print-bytecode.
  • JSC LLInt: low-level interpreter, also visible.
  • Hermes: AOT to .hbc bytecode, ship that instead of JS.

Idioms & style

  • Naming: camelCase for vars/functions, PascalCase for classes/constructors, SCREAMING_SNAKE for module-level constants, _private (convention), #truly-private (class private fields, ES2022).
  • Formatters: Prettier (de facto), Biome (Rust, all-in-one fmt+lint, fast), dprint.
  • Linters: ESLint (v9+ flat config), Biome, oxlint (Rust, fast subset). Common configs: eslint-config-airbnb, @typescript-eslint, eslint-plugin-import.
  • Idiomatic patterns:
    • const by default, let only when reassigning, never var.
    • === / !== over == / != (avoid coercion).
    • Destructuring + default params: function f({ a = 1, b } = {}) {}.
    • Async/await over raw Promise chains.
    • Optional chaining ?. and nullish coalescing ?? over && / || for null-vs-falsy distinctions.
    • Module side effects only when intentional; prefer pure exports.
    • Avoid class when a closure-returning factory is enough.
  • What reviewers flag: var, missing await, floating Promises (no-floating-promises rule), JSON.parse(JSON.stringify(x)) for cloning (use structuredClone), mutating shared state in arrow callbacks, broad catch (e) {}.

Ecosystem

DomainTools
FrontendReact, Vue, Svelte / SvelteKit, SolidJS, Angular, Qwik, Astro, Lit, Preact
Meta-frameworksNext.js, Nuxt, SvelteKit, Remix / React Router 7, Astro, Qwik City
ServerExpress, Fastify, Koa, Hono, NestJS, AdonisJS, ElysiaJS (Bun)
Build / bundleVite (Rolldown-based 2026), webpack, esbuild, Rolldown, Turbopack, Parcel
Type-aware buildtsc, swc, Babel, oxc
TestVitest, Jest, node:test, Mocha, Playwright (e2e), Cypress, Testing Library
StateZustand, Redux Toolkit, Jotai, MobX, Pinia (Vue), Effector
ORM / DBPrisma, Drizzle, Kysely, TypeORM, MikroORM, Mongoose
MobileReact Native, Expo, Capacitor, NativeScript
DesktopElectron, Tauri (Rust shell, JS frontend), Neutralino
Notable usersGoogle (V8, Angular), Meta (React, RN, Hermes), Netflix, Microsoft (TS, VS Code), Vercel, Stripe

Gotchas

  • == coercion: [] == false is true, 0 == "0" is true, null == undefined is true. Use ===.
  • typeof null === "object" — historical bug, never fixed.
  • NaN !== NaN — use Number.isNaN(x) or Object.is(x, NaN).
  • Floating point: 0.1 + 0.2 === 0.3 is false. Use BigInt or fixed-point for money.
  • this rebinding: lost when you pass a method as a callback. Bind it (fn.bind(this)) or use arrows.
  • for...in on arrays — iterates keys as strings, plus inherited enumerable props. Use for...of or forEach.
  • Hoisting: var and function declarations hoist; let/const are in the temporal dead zone until initialized.
  • Implicit globals: assignment to undeclared name in non-strict mode creates a global. Use "use strict" or modules (always strict).
  • Floating Promises: unhandled rejections crash Node by default in 22+. Always await or .catch().
  • Date is awful — month is 0-indexed, mutable, no timezone. Use Temporal (Stage 3) or date-fns / Luxon.
  • JSON limitations: no BigInt, no undefined, no functions, no circular refs, dates become strings.
  • Newcomers from Java/C#: no method overloading, no real privacy except #field, no compile-time type errors without TS.
  • Newcomers from Python: truthiness differs ("", 0, null, undefined, NaN, false are falsy; [] and {} are truthy). Object key order is “integer-like keys ascending, then string keys insertion-order.”

Citations