Cheat sheet · JavaScript Part 2: Objects, Async and Modules
JavaScript Part 2 cheat sheet
The deeper half of JavaScript: prototypes, this and classes, promises, modules, iterators, the DOM, errors and copying, and memory. Rules and traps for the night before an interview.
blogs · jp-blogs.vercel.app/cheatsheets/series/javascript-2
Prototype chain and newLesson 0 →
- An object is a bag of properties plus one hidden
[[Prototype]]link. A read walks the chain untilnull; a write makes an own property. Fn.prototypebecomes the prototype ofnew Fn()objects.__proto__reads or writes any object's link; preferObject.getPrototypeOf.new= create an object fromFn.prototype, runFnwiththis= it, return it (unlessFnreturns another object).
function myNew(Ctor, ...args) {
const obj = Object.create(Ctor.prototype);
const r = Ctor.apply(obj, args);
return r instanceof Object ? r : obj;
}Watch out: obj.count++ on an inherited count creates an own property. obj.list.push(1) on an inherited array changes the shared one. Put mutable defaults in the constructor.
Descriptors, freeze and own keysLesson 0 →
| Call | Add | Delete | Change values |
|---|---|---|---|
preventExtensions | No | Yes | Yes |
seal | No | No | Yes |
freeze | No | No | No |
- All three are shallow. Sloppy mode fails silently; strict mode throws
TypeError. - Descriptor:
value,writable,enumerable,configurable(orget,set). Assignment sets all on;definePropertysets all off. inandfor...insee the whole chain.Object.hasOwn,keys,entriessee own properties only.
How this is decidedLesson 1 →
| Order | Call site | this is |
|---|---|---|
| 1 | new Fn() | The new object |
| 2 | fn.call(x), apply, bind | x |
| 3 | obj.fn() | obj (the receiver) |
| 4 | fn() | undefined (strict) or global |
| Arrow | Anywhere | this of the code around it; fixed |
- fn.call(x, a, b)
- Run now, arguments listed
- fn.apply(x, [a, b])
- Run now, arguments in an array
- fn.bind(x, a)
- Return a new function,
thisfixed, not run
Watch out: const f = obj.method; f() loses this. Fix: wrapper () => obj.method(), bind, or an arrow field. removeEventListener needs the same reference.
Classes, private fields, superLesson 1 →
class Animal {
#age = 0; // truly private
static count = 0;
constructor(name) { this.name = name; }
speak() { return this.name; }
}
class Dog extends Animal {
constructor(name) {
super(name); // before any this
this.tricks = [];
}
}- A class is a function; methods sit on
Class.prototype. Extras over functions: needsnew, always strict, methods not enumerable,#private. #xis enforced and hidden fromkeysandJSON;_xis only a habit.thisbeforesuper()is aReferenceError. Child fields are not set while the parent constructor runs.instanceofchecksCtor.prototypeon the chain. UseArray.isArrayfor arrays. Mixin:const M = Base => class extends Base {}.
Promises and the event loopLesson 2 →
- States: pending, then fulfilled or rejected, once. Handlers always run later as microtasks.
thenreturns a new promise: a returned value fulfills, a returned promise is followed, a throw rejects, a missing handler passes through.- Order: sync code, then
nextTick(Node CJS), then promise microtasks, then timers. Microtasks always beat the next timer.
setTimeout(() => log("timer"), 0);
Promise.resolve().then(() => log("p"));
process.nextTick(() => log("tick"));
log("sync");
// sync, tick, p (promise), timerNode CommonJS. In a browser there is no nextTick.
Combinators and async/awaitLesson 2 →
| Method | Fulfills when | Rejects when |
|---|---|---|
all | All succeed | First failure |
allSettled | All finish (reports each) | Never |
race | First to settle wins | If first settled is a failure |
any | First success | All fail: AggregateError |
const [a, b] = await Promise.all(
[getA(), getB()]);
const ctl = new AbortController();
fetch(url, { signal: ctl.signal });
ctl.abort(); // cancel
AbortSignal.timeout(3000); // TimeoutErrorTwo awaits in a row cost the sum; start both first for the max.
Watch out: forEach does not wait for await. return await is needed inside try/catch. An unhandled rejection ends a Node 15+ process with exit code 1.
CommonJS vs ES modulesLesson 3 →
| Topic | CommonJS | ES modules |
|---|---|---|
| Syntax | require, module.exports | import, export |
| Loaded | Run time, synchronous | Static: link, then run |
| Values | Copy of module.exports | Live bindings |
| Extras | require inside if | Top-level await, tree shaking |
exportsonly points atmodule.exports.exports = {}does nothing;module.exports = {}replaces all.- Node:
.mjsis ESM,.cjsis CJS,.jsfollowspackage.json"type". ESM needs full file extensions. - CJS loads ESM with
await import();require()works on Node 22.12+ unless the module uses top-levelawait.
const m = await import("./chart.js");import() returns a promise of the namespace; bundlers split one file per call. Prefer named exports over default.
Watch out: Cycles: CJS gives a partly filled export; ESM const exports hit the temporal dead zone. Fix by moving shared code to a third module. Tree shaking is blocked by CJS and side effects.
Iterators and generatorsLesson 4 →
- Iterable: has a
Symbol.iteratormethod. Iterator: hasnext()returning{ value, done }. A generator is both. for...ofcallsSymbol.iterator, loopsnext(), and callsreturn()onbreak,returnor throw.for...ingives string keys, inherited too.for...ofgives values and throws on plain objects.next(x)becomes the pausedyield's result; the firstnextargument is dropped.throw()andreturn()finish it.
function* naturals() {
let n = 1;
while (true) yield n++; // infinite
}
const odd = naturals()
.filter(n => n % 2).take(3);
odd.toArray(); // [1, 3, 5] Node 22
async function* pages() { // for await
for (let i = 1; ; i++)
yield await load(i);
}SymbolsLesson 4 →
- Symbol.iterator
- Powers
for...ofand spread - Symbol.asyncIterator
- Powers
for await - Symbol.toPrimitive
- Hint:
default,stringornumber - Symbol.toStringTag
- The X in
[object X] - Symbol.hasInstance
- Customises
instanceof - Symbol.species
- Constructor used by
map/filterin subclasses
Tip: Symbols are unique, hidden from keys and JSON, but not private: getOwnPropertySymbols finds them. Use #private for privacy.
DOM and eventsLesson 5 →
| Pair | Difference |
|---|---|
childNodes / children | All nodes with text / elements only |
querySelectorAll | Static NodeList snapshot |
getElementsByTagName | Live HTMLCollection |
stopPropagation | Stops travel to other elements |
preventDefault | Stops browser action (needs cancelable) |
target / currentTarget | Origin (fixed) / current listener (changes) |
- Phases: capture (down), target, bubble (up). Default listeners run on bubble;
{ capture: true }runs on the way down. - Use
textContentfor untrusted text.innerHTMLcan run code (XSS) and+=destroys listeners. focus,blur,mouseenterdo not bubble; usefocusin,focusout.
Delegation, options and custom eventsLesson 5 →
list.addEventListener("click", e => {
const li = e.target.closest("li");
if (li) select(li);
}, { signal: ctl.signal });
el.dispatchEvent(new CustomEvent(
"app:save",
{ detail: { id: 1 }, bubbles: true }));ctl.abort() removes the listener. dispatchEvent is synchronous. Custom events do not bubble by default.
- Options:
once,capture,passive(promise not topreventDefault, keeps scrolling smooth),signal. removeEventListenerneeds the same function reference; a new arrow never matches.- Setting
.valuefires noinput/change.inputfires per edit;changeon commit. jsdom has no layout (all sizes0).
Errors and async errorsLesson 6 →
class AppError extends Error {
constructor(msg, opts) {
super(msg, opts); // passes cause
this.name = "AppError";
}
}
try { await load(); } // await inside try
catch (e) {
throw new AppError("load", { cause: e });
}
finally { release(); }- Throw
Errorobjects: they havestack,causeandinstanceof. A thrown string has no stack. returninsidefinallyreplaces the earlier return and any error.JSON.stringify(err)is{}:messageandstackare not enumerable. Build{ name, message, stack, cause }.try { asyncFn(); }misses the rejection. Useawaitinsidetry, or.catch.
Proxy and ReflectLesson 6 →
const user = new Proxy({}, {
set(t, k, v, r) {
if (k === "age" && !Number.isInteger(v))
throw new TypeError("age");
return Reflect.set(t, k, v, r);
},
});Reflect does the default and returns true/false; it takes a receiver.
- Traps cover get, set, delete,
in, keys and calls. Use cases: validation, logging reads. - Limits:
#privatefields fail through a proxy, invariants block lies about read-only props, and reads were about 6x slower in the lesson run.
Copying and immutable updatesLesson 6 →
| Tool | Result |
|---|---|
Spread, assign, slice, concat | Shallow: nested objects are shared |
JSON.parse(JSON.stringify(x)) | Dates become strings; undefined, functions, Map lost |
structuredClone | Deep; keeps Date, Map, Set, cycles |
structuredClone limits | Throws on functions, symbols, WeakMap; loses prototype |
const next = { ...state,
user: { ...state.user,
prefs: { ...state.user.prefs,
theme: "light" } } };
const s = arr.toSorted((a, b) => a - b);
// also toReversed, toSpliced, with(i, v)toSorted with no compare function still sorts as text.
Watch out: Object.freeze is shallow: nested edits and a frozen Map's set still work. Use a recursive deepFreeze, or readonly types.
Garbage collection and leaksLesson 7 →
- Freed by reachability from roots (globals, stack, timers, listeners), not by whether you still use it.
- Five usual leaks: unbounded cache, forgotten listener, uncleared timer, closure capturing too much, detached DOM node.
WeakMap/WeakSet: keys are objects, held weakly, not iterable, nosize.WeakRefandFinalizationRegistryare never for correctness.- Buffers live outside the heap: watch
arrayBuffersandrss, not onlyheapUsed.
- Confirm growth in
rssandheapUsed; reproduce in a loop with--expose-gc. - Take two heap snapshots (
--inspectorv8.writeHeapSnapshot()). - Compare, sort by retained size, follow the retainer chain to a root.
- Cut that path, then check memory stays flat.
Interview one-linersLesson 0 →
| Question | Short answer |
|---|---|
__proto__ vs prototype? | prototype is on constructors; __proto__ is any object's actual link. |
| call vs apply vs bind? | Run with listed args; run with array; return a bound function. |
| Class just sugar? | Mostly; plus needs new, strict, #private. |
all vs allSettled? | all fails on the first failure; allSettled reports every outcome. |
| CJS vs ESM? | Run-time copies vs static live bindings. |
| Shallow vs deep copy? | Shallow shares nested objects; structuredClone copies all levels. |
| Map vs WeakMap? | Map keeps keys alive; WeakMap lets them be collected. |
| Memory leak? | Memory you no longer need but can still reach. |
A cheat sheet is a reminder, not a teacher. Each card links to the lesson that explains it step by step.