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
Related posts
Actuator-Aware Observer Inputs for Cascade ADRC of Quadrotors Under Rotor Lag, Measurement Delay and Sensor Noise
Keeping the Franka Emika Panda alive: a ROS 2 stack with a reliable position interface
Rapid co-design of Buoyancy-assisted robots for Challenging Locomotion using Gaussian Evolutionary Specialists
Specification-Aware Distribution Shaping for Robotics Foundation Models
Sensorium: An Open-Source Multimodal Data Collection and Curation Library for Embodied Surgical AI
