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

  1. How JavaScript runs
  2. Variables, scope and hoisting
  3. Data types
  4. Coercion and equality
  5. Operators worth knowing
  6. Strings, numbers and Math
  7. Control flow and loops
  8. Functions and this
  9. Closures
  10. Objects
  11. Arrays
  12. Map, Set, WeakMap, WeakSet
  13. Prototypes and classes
  14. Modules
  15. Asynchronous JavaScript
  16. Error handling
  17. Iterators and generators
  18. JSON, dates and Intl
  19. Regular expressions
  20. Browser essentials (DOM and Web APIs)
  21. Performance and memory
  22. Security basics
  23. ES2015 to ES2026 at a glance
  24. Gotchas that bite everyone
  25. Predict the Output
  26. Interview Rapid Fire
  27. 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 == null to 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

'😀'.length is 2. Length counts UTF-16 units. Use [...str].length to 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)

  1. new Fn(): this is the new object.
  2. fn.call(obj), apply, bind: this is obj.
  3. obj.method(): this is obj.
  4. Plain fn(): this is undefined in strict mode, globalThis otherwise.

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

Related posts