Best Practices & Performance
Code is read far more often than it's written. This lesson collects habits that make JavaScript easier to read, harder to break and faster to run — the kind of things experienced developers look for in code review.
Readability
Use clear names
// ✗ hard to follow const d = u.filter(x => x.a && x.t > 3); // ✓ explains itself const activeLongTermUsers = users.filter(user => user.isActive && user.yearsSubscribed > 3);
Prefer const, then let; never var
A reader seeing const knows the binding never changes. Reserve let for values that genuinely do.
Keep functions small and focused
If a function needs a comment like "now validate the form" halfway through, that half probably wants to be its own function called validateForm.
Return early
function shippingCost(order) { if (!order) return 0; if (order.total >= 50) return 0; if (order.express) return 15; return 5; } console.log(shippingCost({ total: 20, express: true }), shippingCost({ total: 80 }));
15 0
Guard clauses keep the main logic un-nested.
Avoid magic numbers
// ✗ what is 86400000? if (Date.now() - lastSeen > 86400000) logout(); // ✓ const ONE_DAY_MS = 24 * 60 * 60 * 1000; if (Date.now() - lastSeen > ONE_DAY_MS) logout();
Correctness
- Use
===and!==. - Prefer returning new arrays and objects over mutating shared ones, especially in UI frameworks.
- Validate input at the edges (forms, API responses, URL parameters) and fail with clear errors.
- Handle every promise:
awaitit intry/catch, or attach a.catch(). - Use
textContentrather thaninnerHTMLfor anything that came from users.
Organisation
- Split code into ES modules by responsibility:
api.js,cart.js,ui.js. - Keep data logic (calculations, validation) separate from DOM code. Pure functions — same input, same output, no side effects — are easy to test and reuse.
- Let tools enforce style: Prettier for formatting, ESLint for likely bugs. Then code review can focus on logic.
Performance: the DOM is the expensive part
JavaScript itself is fast. Slowness usually comes from doing more work in the page than necessary.
Batch DOM updates
// ✗ touches the live page 1,000 times for (const item of items) { list.innerHTML += `<li>${item}</li>`; } // ✓ builds everything, then inserts once const fragment = document.createDocumentFragment(); for (const item of items) { const li = document.createElement("li"); li.textContent = item; fragment.append(li); } list.append(fragment);
Re-assigning innerHTML += in a loop is particularly slow: it re-parses and rebuilds the entire list every iteration.
Avoid layout thrashing
Reading layout values (offsetHeight, getBoundingClientRect()) right after changing styles forces the browser to recalculate layout immediately. Do all reads first, then all writes. For animation, change transform and opacity, which avoid layout entirely, inside requestAnimationFrame.
Don't block the main thread
Long-running loops freeze the page (lesson 51). Break heavy work into chunks, or move it to a Web Worker. Debounce and throttle high-frequency events like input, scroll and resize (lesson 47).
Performance: loading
- Load scripts with
deferortype="module"so they don't block rendering. - Ship less JavaScript: remove unused libraries, and load rarely used features with dynamic
import(). - Make independent network requests in parallel with
Promise.all. - Cache results that don't change, in memory (a
Map) or with HTTP caching. - Measure before optimising. The Performance and Lighthouse panels in DevTools show what's actually slow.
Choosing the right data structure
const bannedIds = Array.from({ length: 10_000 }, (_, i) => i * 3); const bannedSet = new Set(bannedIds); const userId = 29_997; console.log(bannedIds.includes(userId), bannedSet.has(userId));
true true
Both give the same answer, but includes scans the array item by item, while Set.has jumps straight to the answer. For repeated lookups in large collections, a Set or Map can be thousands of times faster.
Security essentials
- Never insert untrusted strings with
innerHTML,outerHTMLordocument.write. - Never use
eval()ornew Function()on data from users. - Don't store secrets (API keys, passwords) in front-end code — anyone can read it.
- Use
crypto.randomUUID()orcrypto.getRandomValues(), notMath.random(), for anything security-related. - Keep dependencies up to date;
npm auditreports known vulnerabilities.
Accessibility
Interactive JavaScript must work for keyboard and screen-reader users too. Use real <button> and <a> elements for clickable things (they're focusable and respond to Enter/Space), move focus sensibly when opening dialogs, and announce dynamic updates with aria-live regions.