Patterns#
Idioms that show up across well-written JavaScript codebases.
Strict Equality#
Always use === and !==. The loose == performs implicit type
coercion that produces surprising results.
"" == 0 // true
"0" == false // true
null == undefined // true (the only intentionally useful loose case)
Use const by Default#
Reach for const first, let when reassignment is genuinely needed,
var essentially never. const doesn’t make objects immutable; it
just prevents rebinding the name.
Optional Chaining + Nullish Coalescing#
Reach into possibly missing structures without ladders of guards.
const city = response?.user?.address?.city ?? "unknown";
Async Without Await Loops#
Sequential await in a loop serialises requests. To run in parallel:
// Slow: each fetch waits for the previous
for (const url of urls) await fetch(url);
// Fast: all in flight at once
await Promise.all(urls.map((u) => fetch(u)));
If failures shouldn’t abort the rest, use Promise.allSettled.
Modules over Globals#
Export discrete units. Avoid mutating the global object; avoid implicit
shared state. import / export keeps dependencies explicit and
helps tree-shaking.
Immutability for Inputs#
Treat function arguments as read-only. Returning a new object beats mutating a shared one.
function withRole(user, role) {
return { ...user, role }; // new object
}
For arrays, toSorted, toReversed, toSpliced, and with (ES2023)
return new arrays without mutating the original.
Discriminated Objects#
Model variants with a tag field.
const event = { type: "click", x: 10, y: 20 };
switch (event.type) {
case "click": handleClick(event); break;
case "scroll": handleScroll(event); break;
}
Module Side Effects#
Avoid running code (HTTP calls, registrations, mutations) at module load time. Export functions; let the caller decide when to invoke them. This makes modules testable and tree-shakeable.
Error Strategy#
Throw Error (or a subclass); never strings or plain objects. At system
boundaries, catch, log, and translate. In between, let exceptions propagate.
class NotFoundError extends Error {
constructor(id) {
super(`not found: ${id}`);
this.name = "NotFoundError";
this.id = id;
}
}