Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

27 chapters
⌕
Beginner9›
B1Setup & mental modelB2Types & valuesB3Operators & flowB4Functions (first half)B5Objects & arrays (first half)B6DOM & eventsB7Basic asyncB8Errors & tools★Cheat page
Intermediate10›
I1Scope & functions, properlyI2Objects deeplyI3Prototypes & OOPI4Async, properlyI5Modules & toolingI6Regex, dates & APIsI7Error handlingI8Real-time connectionsI9Offline & storage★Cheat page
Advanced10›
A1Engine & memoryA2Advanced asyncA3MetaprogrammingA4Types & dataA5Patterns & architectureA6PerformanceA7SecurityA8EcosystemA9Testing★Cheat page
/ search[ ] chaptert top

JavaScript levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
A8

Ecosystem & professional

The close-out — how the tools around JS actually work, and where the language itself comes from.

TypeScript and Node each have their own shelf on this site, reserved for exactly this kind of depth once they're written — this section stays brief on both, just enough to place them correctly next to plain JS. Testing gets a full chapter of its own, right after this one, because unlike the other two it's squarely inside what "advanced JavaScript" actually means day to day.

TypeScript and Node, briefly

TypeScript adds a type system checked entirely at compile time and erased before anything runs — it's structural, meaning two differently-named types with the same shape are compatible, unlike languages that check by declared name. Generics, narrowing, and the built-in utility types (Partial, Pick, Omit) are where that gets interesting — reserved for the TypeScript shelf once it's written.

Node extends JS past the browser with a filesystem, a process, and no DOM — streams, cluster (multi-process scaling across CPU cores), worker threads, and AsyncLocalStorage (request-scoped context that survives across awaits without threading a parameter through every function) are the parts worth a real chapter — reserved the same way for the Node shelf.

The next chapter covers testing in full: unit vs integration vs E2E, mocking, fake timers, and what coverage does and doesn't actually tell you.

What a bundler is actually doing: ASTs

Every tool in this chapter — a bundler, a linter, a formatter, a minifier, a codemod — starts the exact same way: parse the source into an Abstract Syntax Tree, a plain nested object describing the code's structure, then walk and transform that tree, never the raw text itself.

// A hand-built AST for: const x = 1 + 2;
     // Real parsers (Babel, Acorn) produce something like this automatically.
const ast = {
  type: "VariableDeclaration",
  kind: "const",
  declarations: [{
    type: "VariableDeclarator",
    id: { type: "Identifier", name: "x" },
    init: {
      type: "BinaryExpression",
      operator: "+",
      left: { type: "Literal", value: 1 },
      right: { type: "Literal", value: 2 },
    },
  }],
};
console.log(JSON.stringify(ast.declarations[0].init, null, 2));

That's genuinely close to what astexplorer.net shows for the real thing — every operator, every identifier, every literal is its own typed node. Editing code programmatically (a codemod, the tool behind large automated migrations like a big React version bump across a whole codebase) means finding the right node type in this tree and swapping it, then printing the tree back out to text — never regex-replacing the source directly, which breaks the instant the pattern shows up somewhere the author didn't anticipate (inside a string, a comment, a different context entirely).

A Babel plugin is exactly this walk-and-transform step, packaged: it's handed the AST, given a chance to visit specific node types (ArrowFunctionExpression, ClassDeclaration, …), and returns a modified tree that Babel then prints back to JS — this is the actual mechanism behind "transpile modern syntax down to something older browsers run."

Polyfills vs transpilation — two different problems

These get bundled together in conversation constantly, and they fix genuinely different gaps.

Transpilation Polyfill
Fixes a missing…syntax — arrow functions, optional chaining, classesruntime feature — Array.prototype.flat, Promise, fetch
Howrewrites your source into older-syntax equivalent code, before shippingships extra JS that adds the missing method/object at runtime, if it's not already there
Can it be fixed at build time alone?yes — syntax is fully resolved before the browser ever sees itno — the feature has to actually exist in the running environment, one way or another

core-js is the actual polyfill implementation most tooling pulls from; browser targeting (a browserslist config, shared by most of this toolchain) is what tells both the transpiler and the polyfill loader which engines actually need to be supported — the newer the target list, the less of either gets shipped, which is a direct, measurable bundle-size win for a team that can drop support for old browsers.

Reading the spec, and where new syntax comes from

Every JS feature in every chapter on this site started as a TC39 proposal and moved through five fixed stages before landing in the language:

Stage Means
0 — Strawpersonany committee member's idea, no formal backing yet
1 — Proposalthe problem is real, worth solving, has a champion
2 — Draftreal syntax and semantics written out
3 — Candidatespec-complete, feedback comes from real implementations, not just discussion
4 — Finishedshipped in engines, included in the next yearly ECMAScript edition

Optional chaining, nullish coalescing, and top-level await all went through exactly this pipeline before ever reaching a browser. The official spec (ecma262) reads as dense, formal pseudocode — genuinely worth being able to skim once in a while, because it's the actual final authority any time a blog post's explanation of some edge case and the engine's real behavior disagree.

Say it like this → "When I hit a genuinely ambiguous edge case — something two blog posts explain differently — I check the spec or a quick node -p/console test rather than trust either post. This whole site was built the same way: every runnable claim in it was verified against a real engine first."
Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Write your own Array.prototype.flat4 tests · advancedCount nodes of one type in a tree2 tests · advanced
←previousSecurity↑ CovernextTesting→