JavaScript Tips

Practical JavaScript tips, patterns, and modern ES6+ features.

20 tips

Optional Chaining

Basics

Safely access deeply nested properties without throwing if intermediate values are null/undefined.

obj?.nested?.prop

Nullish Coalescing

Basics

Returns the right operand when the left is null or undefined (but not 0 or empty string).

const val = input ?? "default";

Destructuring with Rename

Basics

Extract object properties into variables with custom names.

const { name: userName, age } = user;

Spread & Rest

Basics

Spread expands iterables; rest collects remaining elements into an array or object.

const { id, ...rest } = obj;
const merged = { ...a, ...b };

Object.groupBy()

ES2024+

Group array items by a key function. Returns an object whose keys are group names.

const grouped = Object.groupBy(users, u => u.role);

Array.toSorted()

ES2024+

Returns a new sorted array without mutating the original. Also: toReversed(), toSpliced().

const sorted = nums.toSorted((a, b) => a - b);

structuredClone()

ES2024+

Deep clone any serializable value — handles nested objects, arrays, Maps, Sets, Dates.

const copy = structuredClone(deepObject);

Array.flat() & flatMap()

Arrays

Flatten nested arrays or map then flatten in one step.

[[1,2],[3,4]].flat(Infinity);
[1,2,3].flatMap(x => [x, x*2]);

Array.at()

Arrays

Access array elements by index — supports negative indices to count from the end.

const last = arr.at(-1);

Array Reduce

Arrays

Common reduce patterns: sum, group-by, flatten, frequency count.

nums.reduce((sum, n) => sum + n, 0);

Template Literals

Strings

Embed expressions in strings with backticks. Supports multi-line strings.

const url = `https://api.com/users/${id}`;

String.replaceAll()

Strings

Replace all occurrences of a substring without regex.

"foo-bar-baz".replaceAll("-", "_"); // "foo_bar_baz"

async/await

Async

Wrap await calls in try/catch for clean error handling.

async function load() {
  try {
    const res = await fetch("/api/data");
    return await res.json();
  } catch (err) { console.error(err); }
}

Promise.allSettled()

Async

Wait for all promises regardless of outcome.

const results = await Promise.allSettled([fetch("/a"), fetch("/b")]);
results.filter(r => r.status === "fulfilled");

AbortController

Async

Cancel fetch requests or any AbortSignal-aware API.

const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);
const res = await fetch(url, { signal: ctrl.signal });

querySelector & classList

DOM

Select elements with CSS selectors and manipulate classes.

const el = document.querySelector(".card");
el.classList.toggle("hidden");

IntersectionObserver

DOM

Detect when elements enter/leave the viewport — ideal for lazy loading.

const obs = new IntersectionObserver(entries =>
  entries.forEach(e => e.target.classList.toggle("visible", e.isIntersecting))
);
obs.observe(el);

Event Delegation

DOM

Attach one listener to a parent instead of many to children.

document.querySelector("ul").addEventListener("click", e => {
  if (e.target.matches("li")) console.log(e.target.textContent);
});

Debounce

Patterns

Delay execution until a pause in events — ideal for search input.

function debounce(fn, ms) {
  let t;
  return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}

Memoization

Patterns

Cache expensive function results based on arguments.

function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (!cache.has(key)) cache.set(key, fn(...args));
    return cache.get(key);
  };
}

All processing happens locally in your browser. No data is sent to any server.