JS

runtime.js

One spine: the evolution of JavaScript

Pick an era, expand a concept, and see where it lands in today's runtimes. Every snippet can be dropped straight into the sandboxed console — the event loop panel then shows real, instrumented execution, not a looping animation.

Module Era

ESM + npm2015 – 2020

Specified in 2015, shipped in runtimes years later. The language finally got a static module format — and the ecosystem got a decade-long CommonJS interop problem.

Two module systems, one language. `require()` is synchronous and dynamic; `import` is static, hoisted, and delivers live bindings. When they meet, each runtime picks its own default-export story — and that is where the ecosystem's strangest edge cases live.

// This whole snippet runs as a module — the sandbox treats files that
// start a line with export as ESM.
export let count = 0; // importers see a *live* binding, not a snapshot
export function inc() {
  count++;
}

console.log("count", count);
inc();
inc();
console.log("count after two inc()", count);

// CommonJS has no live bindings: module.exports is one mutable object and
// require() reads whatever it holds at call time.
//
//   // counter.cjs
//   let count = 0;
//   module.exports = { get count() { return count; }, inc() { count++; } };
Requiring ESM, importing CJS
V8Implements `import`, `export`, and dynamic `import()` — and nothing else. `require` is embedder territory, so no interop logic lives in the engine.
Node.js`import cjs from "pkg"` exposes `module.exports` as the default export, with named exports detected by cjs-module-lexer. `require()` of ESM is experimental in Node 22 and default-on in Node 23+.
DenoNo `require` in the native API — npm packages are reached via `npm:` specifiers, and the Node compatibility layer emulates CJS for `node:` built-ins.
BunTransparent two-way interop: `require()` of ESM and `import` of CJS both work, reconstructing live bindings from CJS where possible.

Interview gotchas

15 genuinely tricky, well-known snippets — event-loop ordering, closures in loops, and `this`-binding traps. Open one, read the gotcha, then drop it straight into the sandboxed console to watch it really run.

Event loop
Closures & scope
this binding
Promises & async
Coercion & equality
Built-in traps