Most JavaScript tutorials are long. Most cheat sheets stop at ES6. This one does neither.
It is written against ECMAScript 2026 (the 17th edition of the spec, June 2026) and MDN, and it works for three kinds of people:
- Beginners and intermediates: read it top to bottom like a crash course.
- Job seekers: jump to Interview Rapid Fire and Predict the Output.
- Working devs: use the table of contents as a quick revision index.
Every concept follows one pattern: what it is, the smallest useful example, and the trap to avoid.
Table of Contents
- How JavaScript runs
- Variables, scope and hoisting
- Data types
- Coercion and equality
- Operators worth knowing
- Strings, numbers and Math
- Control flow and loops
- Functions and this
- Closures
- Objects
- Arrays
- Map, Set, WeakMap, WeakSet
- Prototypes and classes
- Modules
- Asynchronous JavaScript
- Error handling
- Iterators and generators
- JSON, dates and Intl
- Regular expressions
- Browser essentials (DOM and Web APIs)
- Performance and memory
- Security basics
- ES2015 to ES2026 at a glance
- Gotchas that bite everyone
- Predict the Output
- Interview Rapid Fire
- Official resources
1. How JavaScript runs
- JavaScript is single-threaded: one call stack, one thing at a time.
- It is interpreted/JIT-compiled by an engine (V8 in Chrome, Node.js, Deno and Bun; SpiderMonkey in Firefox; JavaScriptCore in Safari).
- It is dynamically typed: variables have no type, values do.
-
ECMAScript is the language spec. Web APIs (
fetch, DOM,setTimeout) are provided by the host (browser or Node), not by the language itself.
| Runs in | Notes |
|---|---|
| Browser | DOM, window, fetch, storage |
| Node.js / Deno / Bun | Files, network, processes, no DOM |
| Edge runtimes | Cloudflare Workers, Vercel Edge (Web APIs, limited Node APIs) |
console.log('Hello, JS'); // run in browser DevTools console, or: node file.js
Strict mode (
'use strict') turns silent mistakes into errors. ES modules and classes are strict by default, so you rarely need the directive today.
2. Variables, scope and hoisting
var |
let |
const |
|
|---|---|---|---|
| Scope | Function | Block | Block |
| Reassign | Yes | Yes | No |
| Redeclare | Yes | No | No |
| Hoisted | Yes, initialized as undefined
|
Yes, but in the Temporal Dead Zone | Yes, but in the TDZ |
const PI = 3.14159; // default choice
let count = 0; // when you need to reassign
// avoid var
const user = { name: 'A' };
user.name = 'B'; // allowed: const protects the binding, not the contents
// user = {}; // TypeError
Scope types: global, function, block ({} with let/const), module.
Hoisting:
console.log(a); // undefined (var is hoisted)
var a = 5;
console.log(b); // ReferenceError (TDZ)
let b = 5;
sayHi(); // works: function declarations are fully hoisted
function sayHi() { console.log('hi'); }
hello(); // TypeError: hello is not a function (var hoisted, value not yet assigned)
var hello = () => {};
Rule of thumb: const by default, let when needed, never var.
3. Data types
7 primitives (immutable, copied by value): string, number, bigint, boolean, undefined, null, symbol.
Everything else is an object (copied by reference): objects, arrays, functions, dates, maps, sets.
| Expression |
typeof result |
|---|---|
'hi' |
'string' |
42, NaN, Infinity
|
'number' |
10n |
'bigint' |
true |
'boolean' |
undefined |
'undefined' |
null |
'object' (historic bug) |
Symbol() |
'symbol' |
[], {}
|
'object' (use Array.isArray) |
() => {} |
'function' |
Array.isArray([]); // true
value === null; // the right null check
value == null; // true for both null and undefined (the one accepted use of ==)
Value vs reference
let a = 1; let b = a; b = 2; // a is still 1 (copied)
const o1 = { x: 1 };
const o2 = o1; // same object
o2.x = 99; // o1.x is now 99
Copying objects
| Method | Depth | Notes |
|---|---|---|
{ ...obj }, Object.assign({}, obj)
|
Shallow | Nested objects are still shared |
structuredClone(obj) |
Deep | Handles Date, Map, Set, circular refs. Fails on functions and DOM nodes. |
JSON.parse(JSON.stringify(obj)) |
Deep, lossy | Drops undefined, functions, turns Dates into strings. Avoid. |
Number gotchas
0.1 + 0.2 === 0.3; // false (0.30000000000000004)
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true: compare floats like this
Number.MAX_SAFE_INTEGER; // 9007199254740991, beyond this use BigInt
NaN === NaN; // false
Number.isNaN(NaN); // true (use this, not isNaN())
9007199254740993n + 1n; // BigInt: arbitrary precision, no mixing with Number
4. Coercion and equality
Use
===always. The only common exception:x == nullto check null or undefined.
== converts types first. === does not.
1 == '1'; // true
1 === '1'; // false
null == undefined; // true
null === undefined; // false
Object.is(NaN, NaN);// true (like === but NaN equals NaN, and +0 differs from -0)
Falsy values (there are exactly 8)
false, 0, -0, 0n, '', null, undefined, NaN
Everything else is truthy, including '0', 'false', [], {}.
Classic coercion results
1 + '2'; // '12' (+ prefers strings)
'3' - 1; // 2 (- forces numbers)
'5' * '2'; // 10
[] + []; // ''
[] + {}; // '[object Object]'
true + 1; // 2
Number(''); // 0
Number('12px'); // NaN
parseInt('12px', 10); // 12 (always pass the radix)
Convert explicitly: Number(x), String(x), Boolean(x) or !!x, parseInt(x, 10), parseFloat(x).
5. Operators worth knowing
| Operator | Meaning | Example |
|---|---|---|
?. |
Optional chaining |
user?.address?.city, fn?.(), arr?.[0]
|
?? |
Nullish coalescing (only null or undefined) | count ?? 10 |
| `\ | \ | ` |
??= `\ |
\ | = &&=` |
** |
Exponent |
2 ** 10 is 1024
|
... |
Spread (expand) or rest (collect) |
[...a, ...b], (...args) =>
|
? : |
Ternary | ok ? 'yes' : 'no' |
typeof, instanceof, in
|
Type checks | 'a' in obj |
1_000_000 |
Numeric separator | readability only |
const port = config.port ?? 3000; // keeps 0 if port is 0
const port2 = config.port || 3000; // replaces 0 with 3000 (bug)
const city = user?.address?.city; // undefined instead of TypeError
6. Strings, numbers and Math
Strings (immutable)
const name = 'World';
`Hello, ${name}! ${2 + 3}`; // template literal, multi-line allowed
| Method | Does |
|---|---|
slice(start, end) |
Substring (negative indexes allowed) |
at(-1) |
Last character |
includes, startsWith, endsWith
|
Boolean checks |
indexOf(x) |
Position or -1 |
split(',') and arr.join(',')
|
String to array and back |
trim(), trimStart(), trimEnd()
|
Remove whitespace |
padStart(5, '0'), padEnd()
|
'7'.padStart(3,'0') gives '007'
|
replace(a, b), replaceAll(a, b)
|
replace with a string only changes the first match |
toUpperCase(), toLowerCase()
|
Case |
repeat(3) |
'ab'.repeat(2) gives 'abab'
|
localeCompare(b) |
Correct sorting for human languages |
isWellFormed(), toWellFormed()
|
Check or fix lone surrogates |
'😀'.lengthis2. Length counts UTF-16 units. Use[...str].lengthto count characters (code points).
Numbers and Math
(1234.5678).toFixed(2); // '1234.57' (returns a STRING)
Number.isInteger(5); // true
Math.round(2.5); // 3
Math.floor(-1.5); // -2
Math.trunc(-1.5); // -1
Math.max(...nums); // Math.max() with no args is -Infinity
Math.floor(Math.random() * (max - min + 1)) + min; // random int in [min, max]
Math.sumPrecise(list) (ES2026) adds many numbers without accumulating rounding error.
7. Control flow and loops
if (x > 0) { } else if (x === 0) { } else { }
switch (day) {
case 'sat':
case 'sun': console.log('weekend'); break; // forget break and it falls through
default: console.log('weekday');
}
| Loop | Use for |
|---|---|
for (let i = 0; i < n; i++) |
Counted loops |
for (const x of iterable) |
Values of arrays, strings, Maps, Sets, generators. Default choice. |
for (const key in obj) |
Enumerable keys of an object (also inherited ones). Avoid on arrays. |
while, do...while
|
Unknown number of iterations |
arr.forEach(fn) |
Side effects. Cannot break, does not wait for await. |
for await (const x of asyncIterable) |
Async streams |
for (const [i, value] of arr.entries()) { }
for (const [key, value] of Object.entries(obj)) { }
for (const [key, value] of map) { }
outer: for (const a of A) { // label to break out of nested loops
for (const b of B) { if (a === b) break outer; }
}
8. Functions and this
Ways to write a function
function add(a, b) { return a + b; } // declaration (hoisted)
const sub = function (a, b) { return a - b; }; // expression
const mul = (a, b) => a * b; // arrow, implicit return
const makeUser = name => ({ name }); // wrap returned object literal in ()
Parameters
function greet(name = 'friend', ...rest) { // default value, rest parameter
return `Hi ${name} (+${rest.length} extra)`;
}
const [first, second] = [1, 2]; // destructuring
function area({ w, h = 1 }) { return w * h; } // destructure a param object
Arrow functions vs regular functions
| Regular | Arrow | |
|---|---|---|
Own this
|
Yes, depends on the call | No, uses surrounding this
|
arguments |
Yes | No (use ...rest) |
Can use new
|
Yes | No |
| Good as object method | Yes | No |
| Good as callback | Sure | Best |
What is this? (4 rules, in priority order)
-
new Fn():thisis the new object. -
fn.call(obj),apply,bind:thisisobj. -
obj.method():thisisobj. -
Plain
fn():thisisundefinedin strict mode,globalThisotherwise.
Arrow functions skip all of this and inherit this from where they were written.
const user = {
name: 'Asha',
hi() { return this.name; },
later() { setTimeout(() => console.log(this.name), 100); }, // arrow keeps this
};
const f = user.hi;
f(); // this is lost
f.call(user); // 'Asha'
const bound = user.hi.bind(user);
bound(); // 'Asha'
fn.call(obj, a, b); // call now, args listed
fn.apply(obj, [a, b]); // call now, args as array
fn.bind(obj, a); // returns a new function, call later
Handy patterns
(() => { /* IIFE: runs immediately, rarely needed with modules */ })();
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); // composition
const curry = a => b => a + b; // currying
function fact(n) { return n <= 1 ? 1 : n * fact(n - 1); } // recursion needs a base case
Higher-order function: takes or returns a function (map, filter, addEventListener).
Pure function: same input, same output, no side effects. Easiest code to test.
9. Closures
A closure is a function that remembers the variables from where it was created, even after that outer function has finished.
function counter() {
let n = 0; // private, nobody else can touch it
return () => ++n;
}
const next = counter();
next(); // 1
next(); // 2
Used for: private state, factories, memoization, debounce, event handlers, callbacks.
The classic loop bug
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3 (one shared var)
for (let i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 0 1 2 (new let per iteration)
Debounce and throttle
function debounce(fn, ms = 300) { // run after the user STOPS for ms
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms = 300) { // run at most once per ms
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) { last = now; fn(...args); }
};
}
Closures can keep big objects alive. Clear timers and listeners you no longer need.
10. Objects
const name = 'Asha';
const user = {
name, // shorthand for name: name
age: 30,
['is' + 'Admin']: false, // computed key
greet() { return `Hi ${this.name}`; },
get label() { return `${this.name} (${this.age})`; },
};
user.name; user['name']; // two ways to read
delete user.age;
'name' in user; // true (includes inherited)
Object.hasOwn(user, 'name'); // true (own property, modern replacement for hasOwnProperty)
Destructuring and spread
const { name: userName, age = 18, ...others } = user; // rename, default, rest
const updated = { ...user, age: 31 }; // copy + override
const merged = { ...defaults, ...overrides }; // later wins
Essential Object methods
| Method | Does |
|---|---|
Object.keys(o), values(o), entries(o)
|
Arrays of own enumerable keys, values, pairs |
Object.fromEntries(pairs) |
Pairs back to an object |
Object.assign(target, ...src) |
Shallow merge (mutates target) |
Object.freeze(o) |
Shallow immutable (silent fail, or TypeError in strict mode) |
Object.groupBy(items, fn) |
Group an array into an object by key (ES2024) |
Object.create(proto) |
New object with a chosen prototype |
Object.defineProperty(o, k, desc) |
Control writable, enumerable, configurable |
Object.getPrototypeOf(o) |
Read the prototype |
const prices = { a: 10, b: 20 };
Object.fromEntries(Object.entries(prices).map(([k, v]) => [k, v * 2])); // { a: 20, b: 40 }
Object.groupBy([1, 2, 3, 4], n => (n % 2 ? 'odd' : 'even')); // { odd: [1,3], even: [2,4] }
Key order: integer-like keys first (ascending), then strings in insertion order.
11. Arrays
Mutating vs non-mutating (memorize this)
| Mutates the original | Returns a new array |
|---|---|
push, pop, shift, unshift
|
map, filter, slice, concat, flat, flatMap
|
splice, sort, reverse, fill
|
toSorted, toReversed, toSpliced, with (ES2023) |
Everyday methods
const nums = [5, 1, 4];
nums.map(n => n * 2); // [10, 2, 8]
nums.filter(n => n > 1); // [5, 4]
nums.find(n => n > 1); // 5 (first match or undefined)
nums.findLast(n => n > 1); // 4
nums.some(n => n > 4); // true
nums.every(n => n > 0); // true
nums.includes(4); // true
nums.reduce((sum, n) => sum + n, 0); // 10 (always give an initial value)
nums.at(-1); // 4
nums.slice(1, 3); // [1, 4]
nums.flat(Infinity); // flatten nested arrays
Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]
[...new Set(nums)]; // unique values
Sorting
[10, 9, 1].sort(); // [1, 10, 9] (default sorts as STRINGS)
[10, 9, 1].sort((a, b) => a - b); // [1, 9, 10]
[10, 9, 1].toSorted((a, b) => b - a); // [10, 9, 1] (original untouched)
people.sort((a, b) => a.name.localeCompare(b.name)); // sort strings properly
Immutable updates
const add = [...arr, item];
const remove = arr.filter(x => x.id !== id);
const update = arr.map(x => (x.id === id ? { ...x, done: true } : x));
const swap = arr.with(2, 'new'); // replace index 2
Real-world pipeline
const total = orders
.filter(o => o.status === 'paid')
.map(o => o.amount)
.reduce((sum, n) => sum + n, 0);
arr.length = 0empties an array.arr[100] = 1on a short array creates holes.Array(3)is empty slots, not[undefined, undefined, undefined].
12. Map, Set, WeakMap, WeakSet
Map vs plain object
Map |
Object | |
|---|---|---|
| Key types | Anything | Strings and symbols |
| Order | Insertion | Insertion (integer keys first) |
| Size | map.size |
Object.keys(o).length |
| Frequent add/remove | Faster | Slower |
| JSON | No direct support | Yes |
const m = new Map([['a', 1]]);
m.set('b', 2).get('b'); // 2
m.has('a'); m.delete('a'); m.size;
for (const [k, v] of m) { }
// count things (ES2026 getOrInsert, check engine support)
const counts = new Map();
for (const w of words) counts.set(w, counts.getOrInsert(w, 0) + 1);
Set (unique values)
const s = new Set([1, 2, 2, 3]); // {1, 2, 3}
s.add(4); s.has(2); s.delete(1); s.size;
Set algebra (ES2025):
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
a.union(b); // {1, 2, 3, 4}
a.intersection(b); // {2, 3}
a.difference(b); // {1}
a.symmetricDifference(b); // {1, 4}
a.isSubsetOf(b); a.isSupersetOf(b); a.isDisjointFrom(b);
WeakMap and WeakSet
Keys must be objects (or non-registered symbols), held weakly so they can be garbage collected. Not iterable, no size. Use for metadata attached to objects without causing leaks.
const meta = new WeakMap();
meta.set(domNode, { clicks: 0 }); // entry disappears when domNode is collected
13. Prototypes and classes
The prototype chain
Every object has a hidden link to another object (its prototype). Property lookup walks that chain until null.
const animal = { eats: true };
const dog = Object.create(animal);
dog.barks = true;
dog.eats; // true (found on the prototype)
Object.getPrototypeOf(dog) === animal; // true
class is cleaner syntax over the same prototype system.
Classes
class Account {
#balance = 0; // truly private
static count = 0; // belongs to the class
constructor(owner) {
this.owner = owner;
Account.count++;
}
deposit(n) {
if (n <= 0) throw new RangeError('Amount must be positive');
this.#balance += n;
return this; // enables chaining
}
get balance() { return this.#balance; }
static create(owner) { return new Account(owner); }
}
class Savings extends Account {
constructor(owner, rate) {
super(owner); // must call super before using this
this.rate = rate;
}
deposit(n) { return super.deposit(n * 1.01); }
}
const s = new Savings('Asha', 0.05);
s instanceof Account; // true
s.balance; // 0
// s.#balance // SyntaxError (private)
Facts:
- Methods live on the prototype (shared). Class fields (
x = 1) live on each instance. - Class bodies are strict mode. Classes are not hoisted like functions (TDZ).
-
#privatefields cannot be read from outside, even by subclasses. Check with#x in obj. -
static { }blocks run once at class definition for setup. - Prefer composition over deep inheritance.
14. Modules
| ES Modules (ESM) | CommonJS (CJS) | |
|---|---|---|
| Syntax |
import / export
|
require() / module.exports
|
| Loading | Static, async | Dynamic, sync |
Top-level await
|
Yes | No |
| Default in | Browsers, modern Node ("type": "module" or .mjs) |
Older Node (.cjs) |
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default class Calculator {}
// app.js
import Calculator, { PI, add as sum } from './math.js';
import * as math from './math.js';
const { default: Chart } = await import('./chart.js'); // dynamic import (lazy load)
import config from './config.json' with { type: 'json' }; // JSON modules (ES2025)
- Browsers:
<script type="module" src="app.js"></script>. - ESM imports are live bindings and are hoisted. Modules run once and are cached.
- Prefer named exports (better refactoring and tree shaking).
- Always include the file extension in browser and Node ESM imports.
15. Asynchronous JavaScript
The event loop (the one diagram to memorize)
Call Stack <--- Microtask queue (Promises, queueMicrotask) runs FIRST, fully drained
<--- Macrotask queue (setTimeout, events, I/O) ONE task, then microtasks again
Order: run sync code, drain all microtasks, run one macrotask, drain microtasks, repeat.
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
queueMicrotask(() => console.log('4'));
console.log('5');
// 1, 5, 3, 4, 2
In Node.js,
process.nextTickruns even before Promise microtasks.
Promises
A Promise is a value that arrives later. States: pending, fulfilled, rejected.
fetch('/api/user')
.then(res => res.json())
.then(user => console.log(user))
.catch(err => console.error(err))
.finally(() => hideSpinner());
| Combinator | Resolves when | Rejects when |
|---|---|---|
Promise.all([...]) |
All fulfilled (array of results) | Any rejects (fail fast) |
Promise.allSettled([...]) |
All settled ({status, value/reason}) |
Never |
Promise.race([...]) |
First settles | First rejection if it is first |
Promise.any([...]) |
First fulfills | All reject (AggregateError) |
const { promise, resolve, reject } = Promise.withResolvers(); // ES2024
Promise.try(() => maybeSyncOrAsync()); // ES2025: sync throws become rejections, result is always a Promise
async / await
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`, { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error(`HTTP ${res.status}`); // fetch does NOT reject on 404 or 500
return await res.json();
} catch (err) {
console.error('Load failed', err);
throw err;
} finally {
hideSpinner();
}
}
- An
asyncfunction always returns a Promise. -
awaitonly pauses that function, not the whole program.
Sequential vs parallel (huge performance difference)
const a = await getA();
const b = await getB(); // waits for A first (slow)
const [a2, b2] = await Promise.all([getA(), getB()]); // both start together (fast)
Common async mistakes
items.forEach(async item => { await save(item); }); // forEach does NOT wait
await Promise.all(items.map(item => save(item))); // correct, parallel
for (const item of items) await save(item); // correct, one by one
Cancel, timeout, retry
const controller = new AbortController();
fetch(url, { signal: controller.signal });
controller.abort(); // cancels the request
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function retry(fn, tries = 3) {
for (let i = 0; i < tries; i++) {
try { return await fn(); }
catch (e) { if (i === tries - 1) throw e; await sleep(2 ** i * 500); }
}
}
Async iteration
for await (const chunk of stream) { } // consume async iterables
const all = await Array.fromAsync(asyncGenerator()); // ES2026: collect an async iterable into an array
fetch cheat
const res = await fetch('/api/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Pen' }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
16. Error handling
try {
JSON.parse('{ bad json');
} catch (err) {
console.error(err.name, err.message); // SyntaxError ...
} finally {
cleanup(); // always runs
}
| Built-in error | Typical cause |
|---|---|
TypeError |
Wrong type, undefined.x, calling a non-function |
ReferenceError |
Variable does not exist (or TDZ) |
SyntaxError |
Bad syntax or bad JSON |
RangeError |
Value out of range, infinite recursion |
AggregateError |
Many errors at once (Promise.any) |
Custom errors and cause
class ValidationError extends Error {
constructor(message, field, options) {
super(message, options);
this.name = 'ValidationError';
this.field = field;
}
}
try {
await save();
} catch (err) {
throw new Error('Could not save profile', { cause: err }); // keeps the original error
}
- You can
throwanything, but throwErrorobjects so you get a stack trace. -
Error.isError(x)(ES2026) is a reliable check that works across realms (iframes), unlikeinstanceof. -
try/catchcatches sync errors andawaited rejections. It does not catch errors inside un-awaited promises or callbacks. - Handle global fallbacks:
window.addEventListener('unhandledrejection', ...)in browsers,process.on('unhandledRejection', ...)in Node.
17. Iterators and generators
An iterable has a [Symbol.iterator]() method that returns an iterator (an object with next() returning { value, done }). for...of, spread and destructuring all use this.
function* range(start, end) {
for (let i = start; i < end; i++) yield i; // pauses here until next() is called
}
[...range(0, 3)]; // [0, 1, 2]
Generators are lazy: they compute values only when asked, so they can be infinite.
Iterator helpers (ES2025)
function* naturals() { let n = 0; while (true) yield n++; }
naturals()
.filter(n => n % 2 === 0)
.map(n => n * n)
.take(5)
.toArray(); // [0, 4, 16, 36, 64]
Helpers: map, filter, take, drop, flatMap, reduce, toArray, forEach, some, every, find. Use Iterator.from(iterable) for other iterables. Iterator.concat(a, b) (ES2026) chains iterators.
18. JSON, dates and Intl
JSON
JSON.stringify({ a: 1, b: [2, 3] }, null, 2); // pretty print
JSON.parse('{"a":1}'); // throws SyntaxError on bad input
JSON.parse(text, (key, value) => key === 'date' ? new Date(value) : value); // reviver
JSON.stringify surprises:
-
undefined, functions and symbols are dropped in objects (becomenullin arrays). -
NaNandInfinitybecomenull. -
Datebecomes an ISO string (viatoJSON), and does not come back as a Date. - Circular references and
BigIntthrow. -
MapandSetbecome{}.
Dates
const d = new Date(); // now
new Date('2026-10-23'); // date-only strings are parsed as UTC
new Date('2026-10-23T10:00'); // date-time without zone is parsed as LOCAL
new Date(2026, 9, 23); // month is 0-based: 9 means October
d.getTime(); Date.now(); // ms since 1970 (Unix epoch)
d.toISOString(); // '2026-10-23T...Z'
-
Dateis mutable, has weak time zone support, and a confusing API. - Temporal is the proposed modern replacement. Several blog posts list it under ES2026, but it is not in the ES2026 spec. Check MDN and caniuse for current engine support before using it.
- For now: store UTC (
toISOString()), format withIntl, or use a small library (date-fns, Day.js).
Intl (formatting without libraries)
new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(1234567.5);
// '₹12,34,567.50'
new Intl.DateTimeFormat('en-IN', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date());
new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(-1, 'day'); // 'yesterday'
new Intl.ListFormat('en').format(['A', 'B', 'C']); // 'A, B, and C'
Also useful: structuredClone(), crypto.randomUUID(), URL and URLSearchParams:
const url = new URL('https://x.com/search?q=js');
url.searchParams.get('q'); // 'js'
url.searchParams.set('page', '2');
19. Regular expressions
const re = /pattern/flags;
const re2 = new RegExp('pattern', 'g');
| Syntax | Meaning |
|---|---|
\d \w \s
|
Digit, word char, whitespace (uppercase = opposite) |
. |
Any char except newline (use s flag to include it) |
^ $
|
Start, end |
* + ? {2,4}
|
0+, 1+, 0 or 1, range (add ? to make it lazy) |
[abc] [^abc] [a-z]
|
Character classes |
(x) (?:x) (?<name>x)
|
Capture, non-capture, named capture |
| `x\ | y` |
(?=x) (?!x)
|
Lookahead (positive, negative) |
(?<=x) (?<!x)
|
Lookbehind |
| Flag | Meaning |
|---|---|
g |
All matches |
i |
Case-insensitive |
m |
^/$ match per line |
s |
. matches newline |
u / v
|
Unicode mode (v adds set operations) |
y |
Sticky |
d |
Match indices |
/\d+/.test('abc123'); // true
'a1b22'.match(/\d+/g); // ['1', '22']
'2026-10-23'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/).groups.y; // '2026'
[...'a1b22'.matchAll(/\d+/g)].map(m => m[0]); // ['1', '22']
'a-b-c'.replaceAll('-', '+'); // 'a+b+c'
const safe = new RegExp(RegExp.escape(userInput), 'i'); // ES2025: escape user text for a regex
A regex with the
gflag keepslastIndexbetween.test()calls, which causes alternating true/false bugs. Do not reuse a global regex with.test().
Beware catastrophic backtracking (ReDoS) on user input with nested quantifiers like(a+)+$.
20. Browser essentials (DOM and Web APIs)
Select and change the DOM
const el = document.querySelector('.card'); // first match
const all = document.querySelectorAll('li'); // NodeList (use for...of)
el.textContent = 'Safe text'; // use this for user data
el.innerHTML = '<b>HTML</b>'; // XSS risk with untrusted input
el.classList.add('active'); el.classList.toggle('open');
el.dataset.id; // reads data-id
el.setAttribute('aria-expanded', 'true');
const li = document.createElement('li');
li.textContent = 'New';
list.append(li);
li.remove();
Events
button.addEventListener('click', e => {
e.preventDefault(); // stop default behavior (link, form submit)
e.stopPropagation(); // stop bubbling
}, { once: true }); // options: once, passive, capture, signal
Phases: capture (top to target), target, bubble (target back up). Most events bubble.
Event delegation (one listener for many children, works for items added later):
list.addEventListener('click', e => {
const item = e.target.closest('li[data-id]');
if (!item || !list.contains(item)) return;
console.log(item.dataset.id);
});
Storage and other APIs
| API | Use | Notes |
|---|---|---|
localStorage |
Persistent key-value | Strings only, sync, about 5 MB, never store secrets |
sessionStorage |
Per-tab storage | Cleared when the tab closes |
| Cookies | Server-readable | Use HttpOnly, Secure, SameSite for auth |
IndexedDB |
Large structured data | Async |
fetch |
HTTP | See section 15 |
IntersectionObserver |
Lazy loading, infinite scroll | Better than scroll listeners |
ResizeObserver, MutationObserver
|
Watch size or DOM changes | |
requestAnimationFrame |
Smooth animation | Runs before the next paint |
AbortController |
Cancel fetch and listeners | |
Web Workers |
Heavy work off the main thread | No DOM access |
localStorage.setItem('user', JSON.stringify(user));
const saved = JSON.parse(localStorage.getItem('user') ?? 'null');
21. Performance and memory
- Do less work on the main thread. Long tasks block clicks and rendering. Move heavy work to Web Workers or break it up.
- Debounce input handlers and throttle scroll/resize handlers (section 9).
-
Batch DOM writes, avoid reading layout (
offsetHeight) between writes. -
Lazy load with dynamic
import()andIntersectionObserver. - Use
Map/Setfor lookups instead ofarr.includes/arr.findinside loops (O(1) vs O(n)). - Do not optimize without measuring: DevTools Performance and Memory tabs.
Garbage collection and leaks
JavaScript frees memory when an object is unreachable. Leaks are things you accidentally keep reachable:
| Leak | Fix |
|---|---|
Forgotten setInterval or listeners |
clearInterval, removeEventListener, AbortController signals |
| Detached DOM nodes still referenced | Null out references |
Accidental globals (x = 1 without declaration) |
Use const/let, strict mode |
| Closures holding huge data | Keep only what you need |
| Unbounded caches | Limit size, or use WeakMap
|
22. Security basics
-
XSS: never put untrusted text into
innerHTML,outerHTML,document.write, oreval. UsetextContent, or sanitize with DOMPurify. Add a Content Security Policy. -
Avoid
eval()andnew Function()with any dynamic input. -
Prototype pollution: merging untrusted JSON into objects with keys like
__proto__can modifyObject.prototype. Validate keys, useMap, orObject.create(null). - Never trust the client. Validate on the server too. Never ship secrets or API keys in frontend code.
-
Tokens: prefer
HttpOnlycookies overlocalStoragefor session tokens. -
Supply chain: commit your lockfile, run
npm audit, review new dependencies, pin versions for critical packages. - ReDoS: be careful with complex regex on user input.
23. ES2015 to ES2026 at a glance
| Edition | Highlights |
|---|---|
| ES2015 |
let/const, arrow functions, classes, modules, template literals, destructuring, spread/rest, Promises, Map/Set, Symbol, generators, for...of
|
| ES2016 |
**, Array.prototype.includes
|
| ES2017 |
async/await, Object.values/entries, padStart/padEnd
|
| ES2018 | Object rest/spread, async iteration, Promise.finally, regex named groups, lookbehind, s flag |
| ES2019 |
flat/flatMap, Object.fromEntries, optional catch binding, trimStart/trimEnd
|
| ES2020 |
?., ??, BigInt, dynamic import(), Promise.allSettled, globalThis, matchAll
|
| ES2021 |
replaceAll, Promise.any, ??= `\ |
| ES2022 | Top-level {% raw %}await, class fields and #private, static blocks, at(), Object.hasOwn, Error cause, regex d flag |
| ES2023 |
toSorted, toReversed, toSpliced, with, findLast, findLastIndex
|
| ES2024 |
Object.groupBy, Map.groupBy, Promise.withResolvers, resizable ArrayBuffer, regex v flag, isWellFormed
|
| ES2025 | Iterator helpers, Set methods (union, etc.), Promise.try, RegExp.escape, JSON modules, regex modifiers, Float16Array
|
| ES2026 |
Array.fromAsync, Error.isError, Iterator.concat, Math.sumPrecise, Uint8Array base64/hex methods, JSON.parse source text access, Map/WeakMap getOrInsert
|
Reality check on ES2026: this table follows the published ES2026 spec. Some articles list Temporal and explicit resource management (
using) as ES2026, but they are not in that spec. Always check MDN compatibility tables or caniuse before shipping the newest features without a fallback.
Gotchas that bite everyone
-
==coercion. Use===. -
typeof nullis'object'. Check with=== null. -
0.1 + 0.2 !== 0.3. Compare with an epsilon or use integers (paise, cents). -
[10, 9, 1].sort()sorts as strings. Pass a comparator. -
varin loops with closures gives surprising output. Uselet. -
Lost
thiswhen passing a method as a callback. Use arrow functions orbind. -
forEachwithawaitdoes not wait. Usefor...oforPromise.all. -
fetchdoes not reject on HTTP errors. Checkres.ok. -
||vs??.0 || 5is5,0 ?? 5is0. -
Shallow copies. Spread does not copy nested objects. Use
structuredClone. -
constdoes not freeze. It only locks the binding. -
parseInt('08')style bugs. Always pass radix, or useNumber(). -
arr.map(parseInt)returns[1, NaN, NaN](the index is passed as radix). -
new Date('2026-10-23')is UTC butnew Date('2026-10-23T00:00')is local. -
Array holes.
new Array(3)is empty,mapskips holes. UseArray.from({ length: 3 }). -
Unhandled promise rejections crash Node and hide bugs. Always
catch. -
Mutating while iterating. Do not
spliceinsideforEach. Build a new array. -
Floating
thisinsetTimeout(obj.method). Wrap in an arrow function.
Predict the Output
Try each one before reading the answer.
console.log([] == ![]); // true ([] == false, '' == 0, 0 == 0)
console.log('5' + 3, '5' - 3); // '53' 2
console.log([1, 2, 3] + [4]); // '1,2,34'
console.log([10, 9, 1].sort()); // [1, 10, 9]
console.log(typeof null, typeof NaN); // 'object' 'number'
console.log(0.1 + 0.2 === 0.3); // false
console.log(['1', '2', '3'].map(parseInt)); // [1, NaN, NaN]
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i), 0);
// 3 3 3
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// A D C B
const user = { name: 'Asha', hi() { return this.name; } };
const f = user.hi;
f(); // this is lost: TypeError in strict mode (undefined.name), or globalThis.name in sloppy mode
async function a() { console.log(1); await null; console.log(2); }
a(); console.log(3);
// 1 3 2 (code after await runs as a microtask)
Interview Rapid Fire
| Question | Short answer |
|---|---|
| What is JavaScript? | A single-threaded, dynamically typed, multi-paradigm language with first-class functions, run by engines like V8. |
var vs let vs const? |
var is function-scoped and hoisted as undefined. let and const are block-scoped with a TDZ. const cannot be reassigned. |
| What is hoisting? | Declarations are registered before code runs. Functions fully, var as undefined, let/const stay in the TDZ. |
== vs ===? |
== coerces types, === does not. Prefer ===. |
| What are primitives? | string, number, bigint, boolean, undefined, null, symbol. Immutable, copied by value. |
| What is a closure? | A function plus the variables from where it was created. Gives private state. |
What is this? |
Set by how a function is called: new, call/apply/bind, method call, default. Arrows use lexical this. |
| Arrow vs regular function? | Arrows have no own this, arguments, or new. Use them for callbacks, not for object methods. |
call vs apply vs bind? |
call and apply invoke now (args listed vs array). bind returns a new function. |
| What is the prototype chain? | Objects inherit through a linked chain of prototypes until null. |
| What is the event loop? | Runs sync code, then all microtasks, then one macrotask, repeat. Makes async work on a single thread. |
| Microtask vs macrotask? | Microtasks: Promise callbacks, queueMicrotask. Macrotasks: timers, events, I/O. Microtasks run first. |
| What is a Promise? | An object for a future value with states pending, fulfilled, rejected. |
Promise.all vs allSettled vs race vs any? |
All fulfill or fail fast. All settle. First to settle. First to fulfill. |
What does async/await do? |
Syntax over Promises. async returns a Promise, await pauses that function until it settles. |
| How do you run async tasks in parallel? | Start them together, then await Promise.all([...]). |
| Shallow vs deep copy? | Shallow copies one level (spread). Deep copies everything (structuredClone). |
null vs undefined? |
undefined means not assigned. null means intentionally empty. |
| What is event delegation? | One listener on a parent that handles child events using e.target. |
| Event bubbling vs capturing? | Bubbling goes target to root (default). Capturing goes root to target. |
map vs forEach? |
map returns a new array. forEach returns nothing, use it for side effects. |
slice vs splice? |
slice copies a portion. splice mutates the array. |
| What is currying? | Turning f(a, b) into f(a)(b). |
| What is debouncing? | Delay the call until the user stops triggering it. |
for...in vs for...of? |
in loops keys (including inherited). of loops values of iterables. |
| What are generators? | Functions that can pause with yield and resume. Lazy sequences. |
Map vs object? |
Map allows any key type, keeps order, has size, and performs better for frequent changes. |
WeakMap use case? |
Attach data to objects without preventing garbage collection. |
| ESM vs CommonJS? | ESM uses import/export, is static and async-capable. CJS uses require, is sync and dynamic. |
| What is a pure function? | Same input gives same output, with no side effects. |
| How do you prevent memory leaks? | Remove listeners and timers, avoid accidental globals, limit caches, use WeakMap. |
| What is strict mode? | Opt-in stricter parsing and error behavior. Default in modules and classes. |
What is Symbol? |
A unique, immutable primitive, used for hidden keys and protocols like Symbol.iterator. |
Object.freeze vs const? |
const locks the binding. freeze blocks changes to the object's own properties (shallowly). |
27. Official resources
- MDN JavaScript Guide and Reference: the best day-to-day reference
- ECMAScript specification: the source of truth (read the intro for a feature history)
- TC39 proposals: what is coming next and at which stage
- javascript.info: the best free deep-dive tutorial
- Exploring JS by Axel Rauschmayer: thorough and modern
- caniuse and web.dev Baseline: browser support
- node.green: Node.js feature support
- Tools: ESLint, Prettier, TypeScript, Vite, Vitest
Final tip
Do not try to memorize this in one go. Build small things (a todo app, a weather widget, a CLI script), return here when something breaks, and the patterns will stick. Master the core (scope, closures, this, the event loop, promises) and everything else gets easier.
If this helped, save it for revision or interview prep. Next up in the series: Next.js.
Found something outdated or wrong? Drop a comment and I will fix it.
Top comments (0)