A quick-reference for the most commonly used JavaScript features. JavaScript is a dynamically typed, JIT-compiled language that runs in browsers and on servers (Node.js). Functions are first-class values, prototype-based inheritance underpins OOP, and the event loop drives asynchronous execution.
Prefer const by default; use let only when the binding will be reassigned. Avoid var — it is function-scoped and hoisted. JavaScript has seven primitive types; everything else is an object.
const x = 42; // block-scoped, cannot be reassigned
let count = 0; // block-scoped, can be reassigned
var legacy = true; // function-scoped, hoisted — avoid
typeof "hello" // "string"
typeof 42 // "number"
typeof 42n // "bigint"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object" ← historical bug, null IS primitive
typeof Symbol() // "symbol"
use === null to check for null; typeof null is misleading
===
0 == "0" // true (coercion)
0 === "0" // false (no coercion)
== triggers implicit type coercion; === checks type and value
const val = input ?? "default";
const city = user?.address?.city;
?? returns right side only for null/undefined, unlike || which triggers on 0, "", false
JavaScript global functions and methods available without any import. These cover type checking, conversion, math, and JSON serialisation.
typeof value // "string" | "number" | "boolean" | ...
Array.isArray(x) // true only for arrays (typeof returns "object")
Number("42") // 42 (NaN if invalid)
String(42) // "42"
Boolean(0) // false
parseInt("42px", 10) // 42 (always pass radix)
parseFloat("3.14") // 3.14
prefer Number() over parseInt() for strict numeric conversion
console.log("info", value)
console.warn("warning")
console.error("error", err)
console.table([{a: 1}, {a: 2}]) // tabular output
console.time("label")
console.timeEnd("label") // elapsed ms
Math.min(1, 2, 3) // 1
Math.max(1, 2, 3) // 3
Math.floor(3.9) // 3
Math.ceil(3.1) // 4
Math.round(3.5) // 4
Math.abs(-5) // 5
Math.random() // [0, 1)
JSON.stringify(obj) // → string
JSON.stringify(obj, null, 2) // → pretty-printed
JSON.parse(str) // → object (throws on invalid JSON)
// safe parse pattern
try {
const data = JSON.parse(raw);
} catch (e) { /* handle */ }
JSON.stringify ignores functions and undefined values; use replacer for custom behaviour
JavaScript supports classic C-style for, for...of for iterables, for...in for object keys, and while/do...while. Prefer for...of over forEach when you need break/continue.
for (let i = 0; i < arr.length; i++) {} // classic, index-based
for (const item of arr) {} // iterate values (arrays, strings, Maps, Sets)
for (const [k, v] of Object.entries(obj)) {} // iterate object key-value pairs
for (const key in obj) {} // iterate own + inherited keys (usually avoid)
for...of works on any iterable; for...in is for object keys (avoid for arrays)
while (condition) { }
do {
// runs at least once
} while (condition);
if (x > 0) { } else if (x < 0) { } else { }
const label = x > 0 ? "positive" : "non-positive";
switch (status) {
case "ok": break;
case "fail": break;
default: break;
}
switch uses === comparison; always include break or return in each case
outer: for (const row of matrix) {
for (const cell of row) {
if (cell === 0) break outer; // exits both loops
if (cell < 0) continue outer; // next row
}
}
labels are rarely needed — restructure with functions when possible
Strings are immutable — all methods return new strings. Template literals support embedded expressions and multi-line strings.
const msg = `Hello ${name}, you have ${count} messages`;
supports any expression inside ${}; can span multiple lines
s.includes("sub")
s.startsWith("pre")
s.endsWith("suf")
s.indexOf("x") // -1 if not found
s.toLowerCase()
s.toUpperCase()
s.trim()
s.trimStart()
s.trimEnd()
s.replace("old", "new")
s.replaceAll("old", "new")
s.split(",") // array of parts
arr.join(", ") // join array into string
s.slice(start, end) // negative indices count from end
s.padStart(10, "0") // pad to length
s.repeat(3) // repeat n times
Sequence → Array, Map → Object or Map, Set → Set. Arrays are objects with numeric keys. Map and Set are purpose-built and preserve insertion order.
arr.map(x => x * 2) // new array, same length
arr.filter(x => x > 0) // new array, subset
arr.reduce((acc, x) => acc + x, 0) // fold to single value
arr.flatMap(x => [x, x * 2]) // map then flatten one level
arr.find(x => x > 5) // first match or undefined
arr.findIndex(x => x > 5) // index or -1
arr.some(x => x > 5) // true if any match
arr.every(x => x > 0) // true if all match
arr.includes(value) // membership test
arr.push(x) // append — returns new length
arr.pop() // remove last — returns element
arr.unshift(x) // prepend
arr.shift() // remove first
arr.splice(i, n) // remove n elements at index i
arr.sort((a, b) => a - b) // in-place sort; always pass comparator
.sort() without a comparator converts to strings — leads to bugs with numbers
obj.key / obj["key"] // get
obj.key = value // set
delete obj.key // remove
"key" in obj // membership test
Object.keys(obj) // array of keys
Object.values(obj) // array of values
Object.entries(obj) // array of [key, value] pairs
Object.assign({}, a, b) // shallow merge
const m = new Map();
m.set("key", value);
m.get("key");
m.has("key");
const s = new Set([1, 2, 3]);
s.add(4);
s.has(2); // true
[...s] // convert to array
Map allows any key type and preserves insertion order
| Interaction | Array |
Object |
Map |
Set |
String |
|---|---|---|---|---|---|
| Access item | arr[i] / arr.at(i) | obj[k] / obj.k | map.get(k) | Unindexed | str[i] / str.at(i) |
| Access last item | arr.at(-1) / arr[arr.length - 1] | Object.values(obj).at(-1) | [...map.values()].at(-1) | [...set].at(-1) | str.at(-1) / str[str.length - 1] |
| Add / Append | arr.push(x) | obj[k] = v | map.set(k, v) | set.add(x) | str += "x" |
| Insert at index | arr.splice(i, 0, x) | N/A (by key) | N/A (by key) | N/A (unordered) | str.slice(0, i) + "x" + str.slice(i) |
| Update item | arr[i] = val | obj[k] = val | map.set(k, val) | set.delete(o); set.add(n) | str.replace(o, n) |
| Update last item | arr[arr.length - 1] = val | N/A | N/A | N/A | str.slice(0, -1) + "c" |
| Remove item | arr.splice(i, 1) | delete obj[k] | map.delete(k) | set.delete(x) | str.replace(sub, "") |
| Pop (remove & get) | arr.pop() (last) | v = obj[k]; delete obj[k] | v = map.get(k); map.delete(k) | v = set.values().next().value; set.delete(v) | last = str.at(-1); str = str.slice(0, -1) |
| Check existence | arr.includes(x) (O(n)) | k in obj / obj.hasOwnProperty(k) (O(1)) | map.has(k) (O(1)) | set.has(x) (O(1)) | str.includes(sub) |
| Clear / Empty | arr.length = 0 | for (let k in obj) delete obj[k] | map.clear() | set.clear() | str = "" |
| Mutable in-place? | Yes | Yes | Yes | Yes | No (Immutable) |
| Ordered? | Yes (0-indexed) | Yes (String key order) | Yes (Insertion) | Yes (Insertion) | Yes (0-indexed) |
Functions are first-class values. Arrow functions inherit this from the surrounding scope — regular functions bind their own this. Use arrow functions for callbacks.
function greet(name) { return `Hi ${name}`; } // hoisted
const greet = function(name) { ... }; // expression
const greet = (name) => `Hi ${name}`; // arrow (implicit return)
const greet = (name) => { return `Hi ${name}`; }; // arrow (explicit)
function greet(name = "World") {
return `Hi ${name}`;
}
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
const merged = [...arr1, ...arr2];
const copy = { ...obj, extra: true };
Math.max(...arr);
function counter() {
let count = 0;
return () => ++count;
}
const inc = counter();
inc(); // 1
inc(); // 2
inner function retains a reference to count; each counter() call gets its own count
JavaScript is single-threaded. async/await is syntactic sugar over Promises — an async function always returns a Promise. Use await only inside async functions.
async function getUser(id) {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// call it
const user = await getUser(42); // inside async context
getUser(42).then(u => console.log(u)); // outside async context
// all must succeed — rejects on first failure
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// wait for all regardless of failures
const results = await Promise.allSettled([fetchA(), fetchB()]);
// first to settle wins
const fastest = await Promise.race([fetchA(), fetchB()]);
fetch("/api/data")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => setLoading(false));
Wrap unpredictable operations in try/catch. finally runs regardless of success or failure — use it for cleanup.
try {
const data = JSON.parse(raw);
} catch (err) {
if (err instanceof SyntaxError) {
console.error("Invalid JSON:", err.message);
} else {
throw err; // re-throw unexpected errors
}
} finally {
cleanup();
}
class NotFoundError extends Error {
constructor(resource) {
super(`${resource} not found`);
this.name = "NotFoundError";
}
}
throw new NotFoundError("User 42");
try {
await riskyFn();
} catch (err) {
// handle error
}
await throws on rejected promises — wrap in try/catch
Modern JavaScript idioms that appear in almost every codebase.
const { name, age = 0 } = user; // object, with default
const { name: userName } = user; // rename on extract
const [first, , third] = arr; // array, skip element
const { a, ...rest } = obj; // rest in object destructure
// named exports
export const PI = 3.14;
export function add(a, b) { return a + b; }
// default export
export default class App { ... }
// import
import App, { PI, add } from "./math.js";
import * as math from "./math.js";
const val = a ?? b; // b only if a is null/undefined
const val = a || b; // b if a is falsy (0, "", false, null)
const val = a && b; // b only if a is truthy
condition && doSomething(); // guard — execute only if condition is true
const x = flag ? "yes" : "no"; // ternary
for (const item of arr) { } // iterable (arrays, strings, maps, sets)
for (const [k, v] of Object.entries(obj)) { } // object iteration
arr.forEach((item, i) => { }); // side effects with index