Skip to main content

Performance, Best Practices & Interview Tips


1. Avoid Unnecessary DOM Access

// BAD: Accesses DOM in every iteration
for (let i = 0; i < 1000; i++) {
document.getElementById("list").innerHTML += `<li>${i}</li>`;
}

// GOOD: Build string first, then update DOM once
let html = "";
for (let i = 0; i < 1000; i++) {
html += `<li>${i}</li>`;
}
document.getElementById("list").innerHTML = html;

// BEST: Use DocumentFragment (no reflow until appended)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement("li");
li.textContent = i;
fragment.appendChild(li);
}
document.getElementById("list").appendChild(fragment);

2. Debounce and Throttle

// DEBOUNCE: wait until user stops typing/acting
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}

const handleSearch = debounce((e) => {
console.log("Searching:", e.target.value); // fires 300ms after last keystroke
}, 300);
input.addEventListener("input", handleSearch);

// THROTTLE: fire at most once per interval
function throttle(fn, limit) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
return fn.apply(this, args);
}
};
}

const handleScroll = throttle(() => {
console.log("scroll:", window.scrollY);
}, 100);
window.addEventListener("scroll", handleScroll);

3. Memoization

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

const expensiveFib = memoize(function fib(n) {
if (n <= 1) return n;
return expensiveFib(n - 1) + expensiveFib(n - 2);
});

expensiveFib(40); // fast on second call!

4. Lazy Loading

// Dynamic import for code splitting
const loadChart = async () => {
const { Chart } = await import("./chart-lib.js");
return new Chart();
};

// Load only when user needs it
btn.addEventListener("click", async () => {
const chart = await loadChart();
chart.render();
});

Best Practices

// 1. Use const by default, let when reassigning
const MAX_RETRIES = 3;
let retries = 0;

// 2. Always use === instead of ==
if (value === null) {}

// 3. Meaningful names
const d = new Date(); // BAD
const currentDate = new Date(); // GOOD

// 4. Avoid global variables — encapsulate
const App = (() => {
let userCount = 0;
return { getCount: () => userCount };
})();

// 5. Early return pattern (guard clauses — avoid deep nesting)
function processUser(user) {
// BAD: deeply nested
if (user) {
if (user.isActive) {
if (user.hasPermission) {
// do work
}
}
}

// GOOD: guard clauses
if (!user) return;
if (!user.isActive) return;
if (!user.hasPermission) return;
// do work (flat and readable)
}

// 6. Named constants over magic numbers
const SECONDS_PER_DAY = 86400;
if (elapsed > SECONDS_PER_DAY) { /* ... */ }

Common JavaScript Interview Questions

Q: Explain the Event Loop

JavaScript is single-threaded. The event loop continuously checks if the call stack is empty, then processes:

  1. Microtask queue first (Promises, queueMicrotask)
  2. Macrotask queue next (setTimeout, setInterval, I/O)
console.log("start");
setTimeout(() => console.log("timeout"), 0); // macrotask
Promise.resolve().then(() => console.log("promise")); // microtask
console.log("end");
// Output: start → end → promise → timeout

Q: What is the difference between == and ===?

// == (loose) performs type coercion
0 == false // true
1 == "1" // true
null == undefined // true

// === (strict) NO coercion
0 === false // false
1 === "1" // false

// ALWAYS use === in production code

Q: var vs let vs const

Featurevarletconst
ScopeFunctionBlockBlock
HoistedYes (undefined)Yes (TDZ)Yes (TDZ)
ReassignableYesYesNo
RedeclarableYesNoNo
Global propYesNoNo

Q: What is NaN and how to check for it?

NaN === NaN;          // false (NaN is not equal to itself!)
typeof NaN; // "number" (it's a number type!)

// Correct ways to check:
Number.isNaN(NaN); // true
Number.isNaN("abc"); // false (stricter than global isNaN)
isNaN("abc"); // true (coerces first)

Q: Tricky Output Questions

// typeof quirks
typeof null // "object"
typeof undefined // "undefined"
typeof NaN // "number"
typeof [] // "object"
typeof function(){} // "function"

// Equality quirks
[] == ![] // true
[] == false // true
null == false // false (null only == undefined)
null == undefined // true

// Addition quirks
1 + "2" // "12" (string concat)
1 - "2" // -1 (numeric)
[1] + [2] // "12" (arrays to string)

// Comparison
"10" > "9" // false (string comparison: "1" < "9")
10 > 9 // true (numeric)

Code Review Checklist

  • Use const/let — no var
  • Use === not ==
  • Handle async errors with try/catch
  • No innerHTML with user input (XSS risk)
  • Debounce expensive event handlers
  • Clean up event listeners and timers
  • Avoid deeply nested callbacks
  • Use meaningful variable names
  • Don't mutate function arguments
  • Provide default parameter values

MCQ — Performance & Interview

Q1: What is the output of typeof null?

A) "null" B) "undefined" C) "object"D) "primitive"

Answer: C — Famous JS bug. typeof null === "object".

Q2: What is the output order?
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

A) 1, 2, 3, 4 B) 1, 4, 2, 3 C) 1, 4, 3, 2 ✅ D) 1, 3, 4, 2

Answer: C — Sync (1, 4) → Microtasks/Promises (3) → Macrotasks/setTimeout (2).

Q3: What is debouncing?

A) Calling a function at regular intervals B) Delaying function execution until a specified time has passed since the last call ✅ C) Limiting the rate at which a function fires D) Cancelling all pending function calls

Answer: B — Throttling limits the rate; debouncing resets the timer on every call.

Q4: What does Number.isNaN("abc") return?

A) true B) false

Answer: BNumber.isNaN() only returns true for the actual NaN value. The global isNaN("abc") returns true because it coerces first.

Q5: What does the new keyword do?

A) Creates an empty object ✅ B) Sets the object's prototype to the constructor's .prototypeC) Calls the constructor function with this = new object ✅ D) Returns the new object ✅

Answer: All of the above