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:
- Microtask queue first (Promises, queueMicrotask)
- 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
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisted | Yes (undefined) | Yes (TDZ) | Yes (TDZ) |
| Reassignable | Yes | Yes | No |
| Redeclarable | Yes | No | No |
| Global prop | Yes | No | No |
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— novar - Use
===not== - Handle async errors with try/catch
- No
innerHTMLwith 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: B — Number.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 .prototype ✅
C) Calls the constructor function with this = new object ✅
D) Returns the new object ✅
Answer: All of the above