JavaScript

Complete JavaScript cheat sheet covering types, scope, closures, async/await, the event loop, DOM manipulation, and ES6+ features.

13 sections31 cards

primitives

7 primitives: string, number, bigint, boolean, undefined, null, symbol

Everything else is an object (arrays, functions, dates, etc.)

Primitives are immutable and compared by value. Objects are compared by reference.

typeof null === "object" — historic bug in JS. Null is not an object.

typeof function(){} === "function" — functions are objects but typeof returns "function".

typeof undefined === "undefined"

NaN is type number. NaN !== NaN — the only value not equal to itself. Check with Number.isNaN(), not isNaN() (which coerces first).

coercion gotchas

== does type coercion. === does not. Always use ===.

"5" == 5 → true. "5" === 5 → false.

null == undefined → true. null === undefined → false.

null == 0 → false. Null only loosely equals undefined.

[] + []"". [] + {}"[object Object]".

+ with a string → concatenation. + with numbers → addition.

Falsy values: false, 0, "", null, undefined, NaN, 0n. Everything else is truthy (including [], {}, "0").

var — function-scoped. Hoisted and initialized as undefined. Can be re-declared. Leaks out of blocks (if, for). Avoid.

let — block-scoped. Hoisted but NOT initialized (temporal dead zone). Can be reassigned. Cannot be re-declared in same scope.

const — block-scoped. Must be initialized at declaration. Cannot be reassigned. But objects/arrays it points to ARE mutable.

Temporal dead zone (TDZ): the period between entering a scope and the let/const declaration line. Accessing the variable in this zone throws ReferenceError.

Hoisting: declarations are moved to top of their scope during compilation. var → hoisted + initialized to undefined. let/const → hoisted but NOT initialized. Function declarations → hoisted entirely (body too). Function expressions → only the variable is hoisted.

Use const by default. Use let when you need to reassign. Never use var.

function types

function foo() {} — function declaration. Fully hoisted. Can be called before definition.

const foo = function() {} — function expression. Not hoisted. Anonymous unless named.

const foo = () => {} — arrow function. No own this, no arguments, no prototype. Cannot be used as constructor.

const foo = () => value — implicit return when no braces.

const foo = ({a, b}) => a + b — destructured params.

parameters

Default params: function greet(name = "World") {}

Rest params: function sum(...nums) {} — collects remaining args into array. Must be last param.

Spread in call: sum(...arr) — expands array as arguments.

arguments — array-like object in regular functions. Contains all passed args. Not available in arrow functions.

JS doesn't enforce param count. Extra args are ignored (or in arguments). Missing args are undefined.

closures

A closure is a function that remembers variables from its outer scope even after that scope has finished executing.

Every function in JS is a closure. The inner function retains a reference to the outer function's variables — not a copy.

Use cases: data privacy (module pattern), factory functions, memoization, event handlers that need state, currying.

Classic gotcha: var i in a for loop closure — all closures share the same i reference. Fix: use let (block-scoped per iteration) or IIFE.

higher-order functions

Functions that take or return other functions.

map, filter, reduce, forEach, find, some, every — all HOFs on arrays.

Currying: transforming f(a, b) into f(a)(b). Useful for partial application.

Composition: compose(f, g)(x) = f(g(x)). Apply functions right to left.

Memoization: cache results of expensive function calls. Return cached result on same input.

this is determined by HOW a function is called, not where it's defined (except arrow functions).

Global context → this = window (browser) / global (Node). In strict mode → undefined.
Method call: obj.method()this = obj.
Regular function call: fn()this = global / undefined (strict).
Arrow function → no own this. Inherits from enclosing lexical scope.
Constructor: new Fn()this = new object.
Event listener → this = element that triggered event.

call(thisArg, a, b) — invoke function with explicit this and arguments listed.

apply(thisArg, [a, b]) — same but arguments as array.

bind(thisArg) — returns new function with this permanently bound. Doesn't invoke.

Arrow functions are the clean solution when you need to preserve outer this inside a callback.

prototype chain

Every object has an internal [[Prototype]] link to another object. Property lookups walk this chain until found or null is reached.

Object.getPrototypeOf(obj) — get prototype.

obj.hasOwnProperty("key") — check own property, not inherited.

instanceof — checks if constructor's prototype exists anywhere in object's prototype chain.

All objects ultimately inherit from Object.prototype.

ES6 classes

Syntactic sugar over prototype-based inheritance. Not a new OOP model.

constructor() — runs on new. Initialize properties here.

extends — inherit from parent class.

super() — call parent constructor. Must call in child constructor before using this.

super.method() — call parent method.

static method() — called on class itself, not instances.

Private fields: #field — truly private, not accessible outside class.

Getters/setters: get name() {} / set name(v) {}

Classes are NOT hoisted like function declarations. Must define before use.

mutating methods

push(...items) — add to end. Returns new length.

pop() — remove from end. Returns removed item.

shift() — remove from start. Returns removed item.

unshift(...items) — add to start. Returns new length.

splice(start, deleteCount, ...items) — remove/insert at position. Mutates in place.

sort(compareFn) — sorts in place. Default is string order ([10, 2, 1].sort()[1, 10, 2]!). Use (a, b) => a - b for numeric.

reverse() — reverses in place.

fill(val, start, end) — fills range with value.

non-mutating methods

map(fn) — transform each item. Returns new array of same length.

filter(fn) — keep items where fn returns true. Returns new array.

reduce(fn, initial) — accumulate to single value. fn(acc, cur, idx, arr).

find(fn) — first item where fn is true. Returns item or undefined.

findIndex(fn) — index of first match. -1 if not found.

some(fn) — true if ANY item passes.

every(fn) — true if ALL items pass.

includes(val) — true if value exists.

indexOf(val) — index of value. -1 if not found.

slice(start, end) — copy portion. End is exclusive.

concat(...arrays) — merge arrays.

flat(depth) — flatten nested arrays.

flatMap(fn) — map then flat(1). Very useful.

join(sep) — array to string.

Array.from(iterable) — convert to array (NodeList, string, Set, Map).

Array.isArray(val) — check if array (typeof returns "object").

creating & manipulating

Shorthand: { name, age } instead of { name: name, age: age }.

Computed keys: { [varName]: value }

Spread: { ...obj1, ...obj2 } — shallow merge. Later keys win.

Object.assign(target, source) — copies own enumerable properties. Mutates target.

Object.keys(obj) — array of own keys.

Object.values(obj) — array of own values.

Object.entries(obj) — array of [key, value] pairs.

Object.fromEntries(entries) — reverse of entries. Useful with Map.

Object.freeze(obj) — make immutable (shallow).

Object.create(proto) — create object with given prototype.

Optional chaining: obj?.prop?.nested — short-circuits to undefined instead of throwing.

Nullish coalescing: val ?? "default" — fallback only for null/undefined (not 0 or "").

destructuring

Object: const { name, age } = person

Rename: const { name: fullName } = person

Default: const { name = "Anonymous" } = person

Nested: const { address: { city } } = person

Array: const [first, second, ...rest] = arr

Skip: const [, second] = arr

In params: function fn({ name, age }) {}

Swap: [a, b] = [b, a]

Destructuring creates new variables — it doesn't modify the original object.

the event loop

JS is single-threaded. The event loop lets it handle async without blocking.

Call stack → executes synchronous code. Web APIs → handle async tasks (timers, fetch, DOM events). Callback queue (macrotask) → setTimeout, setInterval, I/O. Microtask queue → Promise callbacks, queueMicrotask. The event loop picks: run all microtasks first, then one macrotask, then all microtasks again, repeat.

console.log("A"); setTimeout(() => console.log("B"), 0); Promise.resolve().then(() => console.log("C")); console.log("D") → A, D, C, B. C before B because microtasks (promises) run before macrotasks (setTimeout).

promises

A promise represents a future value. States: pending → fulfilled or rejected.

.then(onFulfilled, onRejected) — chain success handler.

.catch(fn) — handle rejection.

.finally(fn) — runs regardless of outcome.

Promise.resolve(val) — wrap value in resolved promise.

Promise.reject(err) — wrap error in rejected promise.

Promise.all([p1, p2]) — wait for ALL. Rejects if ANY rejects.

Promise.allSettled([...]) — wait for ALL, never rejects. Returns array with status+value.

Promise.race([...]) — resolves/rejects with FIRST to settle.

Promise.any([...]) — resolves with first SUCCESS. Rejects only if all fail.

async / await

Syntactic sugar over promises. Makes async code read like sync.

async function — always returns a promise.

await — pauses function execution until promise resolves. Can only use inside async function (or top-level module).

Error handling: wrap in try/catch. The catch block receives the rejection reason.

Parallel with await: don't await sequentially if independent. Use Promise.all:

const [a, b] = await Promise.all([fetchA(), fetchB()])

Await in loops: for...of works. forEach does NOT await — use for...of or Promise.all with map.

selecting elements

document.querySelector(selector) — first match. Returns element or null.

document.querySelectorAll(selector) — all matches. Returns static NodeList.

document.getElementById(id) — fastest single-element lookup.

document.getElementsByClassName() — live HTMLCollection.

element.closest(selector) — walk up DOM and find nearest matching ancestor.

element.matches(selector) — test if element matches selector.

manipulating

element.textContent — get/set text. Strips HTML tags. Safer than innerHTML.

element.innerHTML — get/set HTML. Be careful with user input (XSS risk).

element.setAttribute(name, val) / getAttribute / removeAttribute

element.classList.add/remove/toggle/contains/replace

element.style.property = value — inline styles.

document.createElement(tag) — create new element.

parent.appendChild(el) / parent.prepend(el) / parent.removeChild(el)

element.insertAdjacentHTML("beforeend", html) — insert HTML at position without replacing.

element.remove() — remove itself from DOM.

node.cloneNode(deep) — clone element. deep=true clones children too.

events

element.addEventListener(event, handler, options)

element.removeEventListener(event, handler) — must pass same function reference.

Options: { once: true } — auto-remove after first call. { passive: true } — hint browser won't call preventDefault (better scroll perf). { capture: true } — listen in capture phase.

Event bubbling: events bubble UP the DOM by default (child → parent → document). e.stopPropagation() — stop bubbling. e.preventDefault() — stop default action (form submit, link navigate).

Event delegation: attach ONE listener to parent, check e.target inside. Better for dynamic elements and performance.

e.target — element that triggered event. e.currentTarget — element listener is attached to.

useful DOM APIs

element.getBoundingClientRect() — position and size relative to viewport.

window.scrollY / window.scrollX — scroll position.

element.scrollIntoView({ behavior: "smooth" })

IntersectionObserver — detect when element enters viewport. Lazy loading, infinite scroll.

MutationObserver — watch for DOM changes.

ResizeObserver — watch for element size changes.

document.cookie — read/write cookies (string parsing, painful). Use libraries or cookie APIs.

localStorage.setItem/getItem/removeItem — persistent key-value. Strings only. Synchronous.

sessionStorage — same API, clears when tab closes.

fetch(url, options) — returns a promise that resolves to a Response object.

Two-step: first await fetch (resolves when headers arrive), then await .json() or .text() (resolves when body is fully received).

fetch only rejects on network failure. A 404 or 500 response is NOT a rejection — you must check response.ok or response.status manually.

Common options: method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data)

AbortController — cancel a fetch. Create controller, pass signal to fetch options, call controller.abort().

CORS: browser enforces same-origin policy. Server must send correct headers (Access-Control-Allow-Origin). Fetch can't bypass this — it's a browser security feature.

syntax

Template literals: `Hello ${name}` — multi-line, expression interpolation.

Tagged templates: html`<p>${val}</p>` — function processes template. Used in styled-components, graphql.

Optional chaining: obj?.a?.b — stops at first nullish.

Nullish coalescing: a ?? b — b only if a is null/undefined.

Logical assignment: a ||= b, a &&= b, a ??= b

Numeric separators: 1_000_000 — readable large numbers.

Short-circuit evaluation: condition && doSomething() — runs right side only if left is truthy.

data structures

Map — key-value, any type as key. Ordered. Has size. .set/.get/.has/.delete/.forEach. Better than object for dynamic keys.

Set — unique values only. Any type. .add/.has/.delete/.size. Fast dedup: [...new Set(arr)].

WeakMap / WeakSet — keys must be objects. Keys are weakly referenced (can be garbage collected). No iteration. Used for private data attached to objects.

Symbol: unique primitive. Symbol("desc"). Used for unique object keys that won't clash.

iterators &amp; generators

Iterable: any object with [Symbol.iterator] method. Arrays, strings, Maps, Sets are iterable.

for...of — iterates values of iterables. Unlike for...in which iterates keys.

Generator: function* with yield. Pauses execution, produces values lazily. Returns an iterator.

yield — pause and emit value. Execution resumes on .next().

Use cases: infinite sequences, lazy evaluation, async control flow.

modules

Named export: export const fn = () => {} — import as import { fn } from "./file"

Default export: export default fn — import as import fn from "./file" (any name)

Re-export: export { fn } from "./other"

Import all: import * as utils from "./utils"

Dynamic import: const mod = await import("./file") — lazy load at runtime.

Modules are deferred by default, have their own scope, and run in strict mode.

try { } catch (e) { } finally { } — catch runs on error, finally always runs.

e.message, e.name, e.stack — properties of error object.

Error types: Error, TypeError, ReferenceError, SyntaxError, RangeError, URIError

Custom errors: class ValidationError extends Error { constructor(msg) { super(msg); this.name = "ValidationError" } }

throw new Error("message") — you can throw anything but throwing Error objects is best practice.

Async error handling: try/catch with async/await, or .catch() on promises. Unhandled promise rejections will crash in Node.js.

window.onerror / window.onunhandledrejection — global error catchers in browser.

common gotchas

[] == false → true (coercion). [] === false → false.

0.1 + 0.2 !== 0.3 — floating point. Use toFixed or multiply to integers.

Mutating arrays/objects passed to functions mutates the original (passed by reference).

JSON.parse(JSON.stringify(obj)) — deep clone (but loses functions, undefined, Date becomes string).

for...in on arrays iterates inherited enumerable properties too. Use for...of or forEach.

Callback hell — nested callbacks. Solved by promises/async-await.

setTimeout(fn, 0) — not immediate. Runs after current call stack clears.

useful patterns

IIFE: (function() {})() — immediately invoked. Creates isolated scope. Old pattern for encapsulation.

Module pattern: closure to create private state with public API.

Debounce: delay execution until N ms after last call. Use for search inputs.

Throttle: execute at most once per N ms. Use for scroll/resize listeners.

Observer pattern: subscribe/publish. Decoupled event communication.

Deep clone: structuredClone(obj) — modern, native, handles dates and more.

Mixin: Object.assign(Target.prototype, mixin) — add methods without inheritance.

string methods

slice(s,e)split(sep)trim()trimStart()trimEnd()includes(s)startsWith(s)endsWith(s)indexOf(s)replace(a,b)replaceAll(a,b)toUpperCase()toLowerCase()padStart(n,c)padEnd(n,c)repeat(n)at(-1)matchAll(regex)

Strings are immutable — all methods return new strings.

at(-1) — last character. Negative indexing.

String.raw`\n` — raw string, backslashes not processed.

regex basics

/pattern/flags — literal. Flags: g (global), i (case insensitive), m (multiline), s (dot matches newline).

regex.test(str) — returns boolean.

str.match(regex) — returns matches array.

str.replace(/pattern/g, replacement)

Key patterns: \d digit, \w word char, \s whitespace, ^ start, $ end, + one or more, * zero or more, ? optional, {n,m} range.

Groups: (pattern) capturing, (?:pattern) non-capturing, (?<name>pattern) named.