JavaScript interviews at senior level are not about syntax — they are about depth of understanding. These 50 questions cover the concepts that filter senior candidates from mid-level ones: how the event loop actually works, why closures behave the way they do, what this refers to in different contexts, and how memory leaks happen silently. Each answer explains the why, not just the what.
⚡ TL;DR: Senior JS interviews focus on: event loop and async mechanics, closure and scope, prototypal inheritance, this binding rules, memory management, ES6+ features in depth, and performance optimization. Know how V8 works under the hood.
Closures and Scope — JavaScript interview questions
// Q1: What is a closure and when would you use one?
// A closure is a function that retains access to its outer scope
// even after the outer function has returned.
function makeCounter(initial = 0) {
let count = initial; // Closed-over variable
return {
increment: () => ++count,
decrement: () => --count,
value: () => count
};
}
const counter = makeCounter(10);
counter.increment(); // 11
counter.value(); // 11
// count is inaccessible from outside — private by closure
// Q2: Classic closure bug in loops
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // Prints 3, 3, 3 (not 0, 1, 2)
}
// Fix 1: let (block-scoped, new binding per iteration)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 0, 1, 2
}
// Fix 2: IIFE to capture value
for (var i = 0; i < 3; i++) {
((j) => setTimeout(() => console.log(j), 0))(i); // 0, 1, 2
}The Event Loop — Most Common Senior Question
// Q3: What is the output and why?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2
// Why:
// - 1, 4 = synchronous (call stack)
// - 3 = microtask queue (Promises) — runs BEFORE macrotasks
// - 2 = macrotask queue (setTimeout) — runs after all microtasks
// Q4: What is the difference between microtasks and macrotasks?
// Microtasks: Promise callbacks, queueMicrotask(), MutationObserver
// Macrotasks: setTimeout, setInterval, setImmediate, I/O, UI events
// Microtask queue drains COMPLETELY before next macrotask runsPrototypal Inheritance
// Q5: How does prototypal inheritance work?
const animal = { breathe() { return 'breathing'; } };
const dog = Object.create(animal);
dog.bark = function() { return 'woof'; };
dog.bark(); // 'woof' — own property
dog.breathe(); // 'breathing' — from prototype chain
// Prototype chain: dog → animal → Object.prototype → null
// Q6: Difference between __proto__, prototype, Object.getPrototypeOf
// __proto__: instance property pointing to prototype (deprecated, use getPrototypeOf)
// .prototype: property on constructor functions only
// Object.getPrototypeOf(obj): correct way to get prototype
function Dog() {}
const d = new Dog();
Object.getPrototypeOf(d) === Dog.prototype; // true
Object.getPrototypeOf(Dog.prototype) === Object.prototype; // truethis Binding Rules
// Q7: What does `this` refer to in each case?
// Rule 1: Default binding — global object (or undefined in strict mode)
function alone() { console.log(this); } // window or undefined
// Rule 2: Implicit binding — the object before the dot
const obj = { name: 'obj', getName() { return this.name; } };
obj.getName(); // 'obj' — this = obj
// Rule 3: Explicit binding — call, apply, bind
function greet(greeting) { return greeting + ' ' + this.name; }
greet.call({ name: 'Alice' }, 'Hello'); // 'Hello Alice'
// Rule 4: Arrow functions — inherit this from enclosing scope
const timer = {
count: 0,
start() {
setInterval(() => this.count++, 1000); // Arrow: this = timer
}
};
// Rule 5: new binding — newly created object
function Person(name) { this.name = name; }
const p = new Person('Bob'); // this inside = new object assigned to pHoisting, let/const/var
// Q8: What is hoisting?
// Variables declared with var are hoisted to top of scope and initialized to undefined
// Functions are fully hoisted (declaration + body)
// let/const are hoisted but NOT initialized (Temporal Dead Zone)
console.log(x); // undefined (hoisted)
var x = 5;
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 5;
console.log(fn()); // Works! Function declaration fully hoisted
function fn() { return 'hello'; }
console.log(arrow()); // TypeError: arrow is not a function
var arrow = () => 'hello'; // var hoisted as undefinedMemory Leaks
// Q9: Name 4 common JavaScript memory leak patterns
// Leak 1: Global variables
function bad() { leak = 'I am global'; } // Missing var/let/const
// Leak 2: Forgotten event listeners
const el = document.getElementById('btn');
const handler = () => doSomething();
el.addEventListener('click', handler);
// Fix: el.removeEventListener('click', handler) on cleanup
// Leak 3: Closures holding references
function outer() {
const bigData = new Array(1000000);
return () => bigData[0]; // bigData kept alive by closure
}
// Leak 4: Detached DOM nodes
const detached = document.createElement('div');
const map = new Map();
map.set(detached, 'data'); // Map holds reference even if removed from DOM
// Fix: use WeakMap — key is garbage collected when element removed
const wm = new WeakMap();
wm.set(detached, 'data'); // Auto-cleaned when detached is GC'dTop 15 Quick-Fire Questions with Answers
- Q10: == vs === — == coerces types; === checks type AND value. Always use ===.
- Q11: null vs undefined — undefined = declared but not assigned; null = intentionally empty. typeof null === ‘object’ (historical bug).
- Q12: What is the Temporal Dead Zone? — The period between entering scope and the let/const declaration. Accessing the variable throws ReferenceError.
- Q13: How does Array.prototype.map work? — Creates new array by calling callback on each element. Skips holes in sparse arrays. Returns undefined for missing returns.
- Q14: What is a Promise? — An object representing eventual completion/failure of async operation. States: pending → fulfilled or rejected. Immutable once settled.
- Q15: Difference between call, apply, bind? — call(thisArg, …args): invoke immediately. apply(thisArg, [args]): invoke with array. bind(thisArg): returns new function.
- Q16: What is a Symbol? — Unique primitive. Symbol(‘desc’) !== Symbol(‘desc’). Used for non-enumerable object keys, well-known symbols like Symbol.iterator.
- Q17: WeakMap vs Map? — WeakMap keys must be objects; keys are weakly referenced (GC can collect). Not iterable. Use for private data attached to objects.
- Q18: What is event delegation? — Attaching one listener to parent instead of many to children. Uses event bubbling. More efficient for dynamic lists.
- Q19: Difference between deep and shallow copy? — Shallow: {…obj} copies top-level only, nested objects still reference same memory. Deep: structuredClone() or JSON.parse(JSON.stringify()) creates fully independent copy.
- Q20: What is a generator? — Function* that can pause (yield) and resume. Returns an iterator. Used for lazy evaluation, infinite sequences, and async control flow.
- Q21: What is optional chaining (?.) ? — Short-circuits to undefined if left side is null/undefined. obj?.a?.b?.c avoids nested null checks.
- Q22: What is nullish coalescing (??)? — Returns right side only if left is null or undefined. Unlike ||, does not trigger for 0, ”, or false.
- Q23: How does async/await work internally? — Syntactic sugar over generators + Promises. async function returns a Promise. await pauses function execution, yields control to event loop.
- Q24: What is tree shaking? — Dead code elimination by bundlers (Webpack, Rollup). Requires ES modules (import/export). CommonJS (require) cannot be tree-shaken statically.
These interview concepts connect directly to the async/await patterns guide and the event loop deep dive — knowing the concepts is not enough, you need to know how they affect real production code. External reference: MDN Event Loop documentation.
Free Weekly Newsletter
🚀 Don’t Miss the Next Cheat Code
Related: TypeScript for JavaScript Developers: The Migration Guide That Actually Works · JavaScript Closures, Scope, and the Module Pattern: The Complete Guide · JavaScript Promises: The Complete Guide From Basics to Advanced Patterns
Related: Go Concurrency Patterns: Goroutines, Channels, and the Patterns Senior Engineers Use · Git Mastery: The Commands and Workflows That Senior Engineers Actually Use · JavaScript Array Methods: Every Method Explained With Real Examples
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
