What you'll learn
By the end of this you'll answer the JavaScript questions that still trip strong developers — closures in loops, event loop ordering, this binding, prototype lookup, and async sequencing — without hand-waving. You'll also know why the language behaves that way, which is how you handle variants you never memorized.
Framework interviews bottom out here eventually. React confusion is often closure confusion wearing a JSX hat. Budget time for console practice, not only flashcards.
Who this is for
- Mid-level frontend, full-stack, or Node backend candidates
- Developers who use JavaScript daily but get surprised by output questions
- Anyone preparing after a round that included "what prints?"
You can skip this if the role is narrowly typed (generated SQL only, no app code) — rare for JS-heavy teams.
What "deep cuts" means in JS interviews
Plain English: interviewers ask how the runtime executes code, not just whether you know syntax.
They might paste ten lines with setTimeout, Promises, and console.log and ask for order. Or ask why var in a loop breaks callbacks. These questions test whether you have a mental model or you're guessing.
Prerequisites
- Variables, functions, objects, arrays
- ES6+:
let/const, arrow functions, destructuring, modules - Basic DOM if frontend — optional for pure Node roles
You don't need to have read the spec. You do need to have been confused at least once by this — that confusion is the curriculum.
Setup from zero
Step 1 — Run output puzzles in Node or browser console
Type them; don't read answers first. Closures, loop + setTimeout, Promise ordering. Muscle memory for the event loop beats flashcards.
Step 2 — Explain one concept to a rubber duck
Pick closures. Two minutes: definition, loop bug, fix with let. If you ramble, simplify.
Step 3 — Connect to a bug you shipped
Stale state, wrong this, missing await. Interviewers lean in when you tie theory to real pain.
Little tip: for "what prints?" questions, say the rule first ("sync, then microtasks, then macrotasks"), then apply it line by line. Interviewers prefer reasoning over lucky guesses.
The mental model
Scope is fixed where code is written; this is fixed by how code is called (except arrow functions, which inherit lexical this).
Closures capture variables from outer scopes. this in a regular function depends on call site: obj.method(), new, call/bind, or undefined/global in sloppy standalone calls.
Most "weird JavaScript" is those two rules interacting with asynchronous scheduling.
Key terms
Closure — function plus remembered outer variables.
Prototype chain — lookup path through [[Prototype]] links.
Hoisting — declarations processed before execution; let/const in temporal dead zone until initialized.
Microtask — Promise callbacks, queueMicrotask.
Macrotask — setTimeout, I/O callbacks (in browser; similar idea in Node).
TDZ — accessing let/const before declaration throws.
IIFE — immediately invoked function expression; classic pre-let fix for loop closures, still fair game in legacy code questions.
Strict mode — 'use strict' changes this in plain calls and catches silent errors; mention if asked about sloppy vs strict behavior.
Step-by-step
Q: What is a closure?
Answer: A function that retains access to outer scope variables after the outer function finished.
function makeCounter() {
let n = 0;
return () => ++n;
}
const c = makeCounter();
c(); // 1
c(); // 2
Follow-up — loop bug:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 3, 3, 3 — one shared binding
Fix: let i (per-iteration binding) or IIFE capturing i.
---
Q: Event loop ordering?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 1, 4, 3, 2
Sync first, microtasks drain, then macrotasks.
---
Q: Explain this.
Answer: Determined by invocation: method call → receiver object; new → new instance; call/apply/bind → explicit; plain call → undefined (strict) or global. Arrow functions: lexical this from enclosing scope.
Trap: const fn = obj.method; fn() loses receiver — bind or wrap in arrow.
---
Q: == vs ===?
Answer: === no coercion; == coerces types. Default to ===. Know null == undefined is true by spec.
---
Q: Prototypal inheritance?
Answer: Objects delegate to prototypes. class syntax sets up the same chain.
const base = { greet() { return 'hi'; } };
const obj = Object.create(base);
obj.greet(); // 'hi' via prototype
---
Q: async/await vs Promises?
Answer: Sugar over Promises. Sequential await in loop vs Promise.all for parallel. Forgetting await returns a pending Promise — silent bugs.
---
Q: What is hoisting?
Answer: Declarations processed before execution in a scope. Function declarations fully hoisted. var hoisted as undefined. let/const hoisted but in TDZ until line runs — access before declaration throws ReferenceError.
Follow-up: "Why does typeof undeclaredVar not throw but typeof letBeforeDeclare before the line throws?" — typeof on undeclared names is special-cased; TDZ still applies to let.
---
Q: What are symbols used for?
Answer: Unique property keys that won't collide with other properties — useful for "hidden" metadata on objects, well-known symbols like Symbol.iterator for custom iteration. Rare in day-to-day app code but fair game in deep-cut rounds. Symbol('a') !== Symbol('a') — each symbol is unique.
---
Q: typeof null === 'object' — why?
Answer: Historical bug in the original type tag representation. Fixing it would break old code relying on the behavior. Interviewers want you to know it's legacy, not logic.
---
Q: Explain instanceof.
Answer: Walks prototype chain — obj instanceof Constructor checks if Constructor.prototype appears in obj's chain. Breaks across realms/iframes with different globals. For plain objects, Array.isArray is safer than instanceof Array in some edge cases.
Working examples
Parallel vs sequential fetch
// Sequential — slow
for (const id of ids) {
await fetchUser(id);
}
// Parallel — faster
await Promise.all(ids.map(id => fetchUser(id)));
Binding this in a class
class Logger {
constructor() { this.prefix = '[app]'; }
log(msg) { console.log(this.prefix, msg); }
}
const l = new Logger();
setTimeout(() => l.log('ok'), 0); // works
setTimeout(l.log, 0); // breaks — pass () => l.log('ok') or bind
Module scope vs global
// module.mjs — each file is its own scope
export const config = { apiUrl: '/api' };
// no accidental window pollution unlike old script tags
Patterns and when to use them
| Pattern | When |
|---------|------|
| let in loops with async callbacks | Per-iteration binding |
| Arrow for callbacks needing lexical this | Class methods passed to timers |
| Promise.all | Independent parallel async work |
| Promise.allSettled | Need all outcomes, some may fail |
| Module scope | Avoid global pollution |
Little tip: mentioning typeof null === 'object' as a historical tag bug — and that it cannot be fixed without breaking the web — shows depth beyond meme answers.
Common mistakes
"this is always the object before the dot." Lost when method extracted.
Confusing closure with scope. Scope is where vars live; closure is retention after outer function returns.
Ignoring microtasks in output questions. Most wrong answers skip Promise order.
Only knowing async/await syntax. Cannot explain Promise.race/all use cases.
Spreading myths about const. const prevents rebinding the variable, not mutating object properties — a common follow-up.
Missing optional chaining in mental model. obj?.a?.b short-circuits on nullish — useful when discussing defensive API code in interviews.
Troubleshooting
Trick question panic: Narrate rules aloud; interviewers often care about process.
TypeScript role: Add structural typing, narrowing — but runtime questions stay JS.
Forgot exact output: Derive from rules; partial credit beats silence.
Deep recursion question: Mention stack overflow; tail calls not guaranteed optimized in all JS engines — iterative or trampoline if asked.
Module question: ESM is static, enables tree-shaking; CJS is sync require — same interop story as Node interviews when bundlers involved.
Interview whiteboard without console: Talk through expected output line by line; write microtask/macrotask labels next to each async line — visual structure helps you and the interviewer.
Checklist
- [ ] Can explain closure + loop fix
- [ ] Can order sync/micro/macrotask example
- [ ] Can list four
thisbinding rules - [ ] Know
Promise.allvs sequentialawait - [ ] Ran five console puzzles this week
- [ ] Connected one concept to a real bug story
- [ ] Can explain TDZ vs hoisting in one sentence
- [ ] Practiced
thisbinding with three call patterns
Practice task
Without running code, predict output for a ten-line snippet mixing setTimeout(0), two Promises, and sync logs. Then run it. Wrong? Rewrite the rules you violated on an index card.
Add a second card for this: write three call patterns (method, standalone, arrow callback) and what this is in each. Review before any JS-heavy round.
FAQ
Still test var?
Sometimes — loop + closure classic uses it.
Generators/async iterators?
Occasional senior question; know they exist.
TypeScript in same round?
Often follows JS; know types don't change runtime behavior.
DOM event loop vs Node?
Browser adds rendering; micro/macrotask idea similar enough for interviews.
WeakMap/WeakSet?
Occasional senior question — weak references don't prevent GC; useful for metadata on objects without leaking memory.
Currying and partial application?
Sometimes asked alongside closures — currying transforms f(a,b) into f(a)(b); shows functional style understanding, not required everywhere.
BigInt and Number?
Mixed arithmetic throws — edge case for financial apps; typeof 1n === 'bigint' if symbols/bigints come up.
What to learn next
- React interview questions — hooks are closures in costume
- Node.js interview prep — same event loop, server context
- AI tools angles — verify JS output instead of trusting generated code
Related on Baseline
- [React interview questions](/interview/react-interview-questions)
- [Node.js interview prep](/interview/nodejs-interview-prep)
- [System design interview starter](/interview/system-design-interview-starter)
Takeaways
JavaScript deep cuts test runtime models, not trivia. Closures, scheduling, and this cover most "gotcha" questions.
Practice in the console, explain rules before answers, and link concepts to bugs you've seen. That combination reads as senior even at mid level.
If you remember only one thing: sync code runs to completion, then microtasks (Promises), then macrotasks (setTimeout) — say that before you predict any output question.