Cheat sheet

The memorizable facts, condensed. Everything here is original material summarizing the official documentation — verify details against it before relying on them in production.

Equality and conversion, one rule each

Use === everywhere except the idiomatic x == null, which is true for exactly null and undefined. The rest of this table is what == is hiding.

Expression Result and reason
typeof null 'object' — a first-release tagging bug; test with value === null.
typeof NaN 'number' — NaN is an IEEE-754 value, and the only one not equal to itself.
null == undefined true, and both are loosely equal to nothing else — not 0, not false, not "".
null >= 0 true, while null > 0 and null == 0 are false: relational operators convert, equality does not.
[] == false true — [] becomes "" becomes 0, and false becomes 0. But [] is truthy in an if.
'5' + 3 '53' — binary + is the only arithmetic operator that concatenates; '5' - 3 is 2.
0.1 + 0.2 0.30000000000000004 — compare with Number.EPSILON, or store money as integer cents.
Number(null) 0, while Number(undefined) is NaN and Number("") is 0.
Object.is(NaN, NaN) true. Object.is differs from === only for NaN and for +0 versus -0.
[NaN].includes(NaN) true (SameValueZero), while indexOf never finds NaN (strict equality).
[10, 9, 1].sort() [1, 10, 9] — the default comparator sorts stringified values; pass (a, b) => a - b.
1n + 1 TypeError — BigInt never mixes with Number in arithmetic, though 1n == 1 is true.

Falsy values and the nullish family

Value or operator Behavior
falsy set false, 0, -0, 0n, "", null, undefined, NaN — plus the legacy document.all. Nothing else.
[] and {} Truthy, both of them. Check .length or Object.keys(o).length instead.
a || b Falls through on every falsy a, so a legitimate 0 or "" is replaced.
a ?? b Falls through only on null and undefined — the fix for the 0 case.
a?.b.c Short-circuits the whole chain to undefined when a is nullish; never yields null.
a ||= b / a ??= b Logical assignment; ??= only assigns when a is null or undefined.

Scope, hoisting and closures

Concept What to say in an interview
var Function-scoped, hoisted and initialized to undefined; leaks out of blocks.
let / const Block-scoped and hoisted but uninitialized — the temporal dead zone throws ReferenceError.
function decl Hoisted with its body, so it is callable above its own line; a function expression is not.
closure A function plus the live bindings it captured — bindings, not copies of their values.
for (let i...) One fresh binding per iteration, which is why the callbacks log 0, 1, 2 instead of 3, 3, 3.
IIFE The pre-module way to get a private scope; a module gives every file one for free.
closure leak Sibling closures share one context, so any live callback can pin a large captured object.
fn.length Parameter count before the first default or rest parameter — the arity libraries inspect.

this, prototypes and classes

Binding precedence, strongest first: new, then bind, then the call-site receiver, then the default. An arrow function ignores all four and uses the enclosing scope.

Form What this is
obj.fn() obj — the receiver at the call site, decided by how the function is called.
const f = obj.fn; f() undefined in strict mode (all class bodies and modules) — the classic extracted-method bug.
arrow function Inherited lexically; arrows also have no arguments, no super and no new.target.
fn.call / fn.apply Invoke now with an explicit receiver; apply takes an array of arguments.
fn.bind(obj) Returns a new function with this fixed permanently — later call or apply cannot change it.
new Foo() A fresh object linked to Foo.prototype, returned unless the body returns another object.
class field = () => {} One pre-bound closure per instance; a prototype method is shared but not bound.
#private Engine-enforced: outside access is a SyntaxError, and #x in obj is the brand-check guard.
super.m() Looks up from the method's [[HomeObject]], not from this — which is why copied mixins lose it.

Async: ordering, combinators and cancellation

One rule explains most output-order puzzles: the microtask queue drains completely between tasks, and everything before the first await runs synchronously.

Tool Behavior
microtask Promise handlers and queueMicrotask — run before the next task and before paint.
task setTimeout, setInterval, I/O callbacks and events — one per event-loop turn.
new Promise(fn) The executor runs synchronously; only the handlers are deferred.
await x Yields to the microtask queue even when x is not a promise.
Promise.all Rejects on the first rejection; the other promises keep running uncancelled.
Promise.allSettled Never rejects — one {status, value} or {status, reason} per input.
Promise.race Settles with the first settled input, fulfilled or rejected; the loser is not cancelled.
Promise.any First fulfillment wins; rejects with an AggregateError only if all inputs reject.
AbortController Pass signal to fetch, then abort() to reject with an AbortError; AbortSignal.timeout(ms) for deadlines.
forEach + async Never awaits. Use for...of for sequential work, or Promise.all(items.map(fn)) for parallel.
fetch + 404 Fulfills. Only network failures reject — check response.ok before parsing the body.

TypeScript: narrowing and the type-level toolbox

Feature What it does
unknown Assignable from anything, usable after a check — the safe any for JSON and catch clauses.
never The empty type; assigning the subject to it in a default branch gives an exhaustiveness check.
x is Foo A type predicate: the checker trusts it without verifying the body, so keep the body honest.
as const Freezes inference to literal types, so mode stays 'dark' instead of widening to string.
satisfies Verifies against a type while each property keeps its own inferred type; add as const to keep literals.
as T Compile-time only. No runtime check, no conversion; as unknown as T is the escape hatch and the smell.
keyof / T[K] Union of keys, and indexed access — together they type a generic property getter.
typeof value In a type position, the inferred type of a binding: type Mode = (typeof MODES)[number].
Partial / Required Add or remove ? on every property — shallow, one level only.
Pick / Omit Keep or drop listed keys. Omit does not constrain its keys to keyof T, so typos pass silently.
ReturnType / Awaited Read a function's return type; compose them for an async payload type.
T extends U ? A : B Distributes over unions unless both sides are wrapped in tuples: [T] extends [U].
infer U Captures a type from the matched position — the machinery behind ReturnType and Parameters.

Modules, iteration and generators

Feature Behavior
ESM import Static, hoisted, top-level only, and a live binding — the importer sees later updates.
require Synchronous, runs at call time, and copies values; no __dirname in ESM (use import.meta.url).
import() Returns a promise for the namespace object — the code-splitting boundary bundlers cut on.
circular ESM Bindings exist but may be uninitialized: functions survive, const throws from the TDZ.
top-level await Defers the module and every importer of it — cheap in config, costly on a critical path.
module instance One per resolved specifier: module-level state is a singleton across all importers.
Symbol.iterator Makes a value work with for...of and spread; a generator method implements it in one line.
generator Lazy iterator: nothing runs until next(), and next(v) passes a value back into the yield.
yield* Delegates to another iterable, forwarding next, throw and return as well.
early break Calls the iterator's return(), so a generator finally block still runs its cleanup.
for await...of Drives an async iterable — the natural shape for paginated APIs and streams.
iterator helpers ES2025 .map/.filter/.take on iterators: lazy, so an infinite source stays usable.

Independent community study resource — not affiliated with or endorsed by Oracle, Microsoft or Ecma International. JavaScript is a trademark of Oracle Corporation; TypeScript is a trademark of Microsoft Corporation. All questions and study notes are original, written from MDN, the ECMAScript specification and the TypeScript handbook. Everything runs in your browser; nothing you answer is stored or transmitted.