1. Overview
This topic gathers the kind of code-tracing and short-answer questions that recur across JavaScript course exams. Rather than testing definitions in isolation, most of these questions ask you to predict exact output or explain why a piece of code behaves the way it does — the same skill tested in technical interviews. Work through each one by tracing the code by hand before reading the given answer, then use the Quick Reference to review right before your exam.
Cricket analogy: Like a cricket exam asking you to predict the exact outcome of a specific over ball-by-ball rather than just define 'yorker,' testing the same hand-tracing skill scouts use to read a match.
2. Frequently Asked Questions
What does this loop print — var vs let inside setTimeout?
javascript
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
Output: 3, 3, 3, 0, 1, 2. With var, there is a single shared binding for i across all iterations, so by the time the callbacks run (after the loop finishes), i is 3 for all of them. With let, each iteration gets its own fresh binding for j, so each callback captures the value from its own iteration: 0, 1, 2`.
Cricket analogy: Like three fielders all told to remember 'the current over number' from one shared scoreboard (var) so they all shout '3' after the innings ends, versus three fielders each given a private notecard (let) recording their own over: 0, 1, 2.
What does typeof return for these values: [], {}, null, function(){}, undefined?
typeof [] → 'object' (arrays are objects); typeof {} → 'object'; typeof null → 'object' (a long-standing language quirk); typeof function(){} → 'function'; typeof undefined → 'undefined'. To distinguish an array from a plain object, use Array.isArray(value) rather than typeof.
Cricket analogy: Like a scorer's ledger classifying every fielding position as simply 'on the field' (typeof object) even though a wicketkeeper and a slip fielder are very different roles — you need a specific check, not the general label.
Trace the output of chained array methods: map, filter, reduce.
javascript
const result = [1, 2, 3, 4, 5]
.map(n => n * 2)
.filter(n => n > 4)
.reduce((sum, n) => sum + n, 0);
console.log(result);
Trace: map doubles each value → [2, 4, 6, 8, 10]. filter keeps values greater than 4 → [6, 8, 10]. reduce sums them starting from 0 → 6 + 8 + 10 = 24. Output: 24`.
Cricket analogy: Like doubling every batter's run tally (map), keeping only those above a cutoff for the highlights reel (filter), then summing the survivors for a team total (reduce) — [1,2,3,4,5] doubles to [2,4,6,8,10], filter keeps [6,8,10], reduce sums to 24.
What is the difference in hoisting between a function declaration and a function expression?
A function declaration (function foo() {}) is fully hoisted — it can be called before its textual position in the code. A function expression assigned to a variable (const foo = function() {} or const foo = () => {}) only hoists the variable binding (as undefined for var, or into the TDZ for let/const), not the function body, so calling it before the assignment line throws a TypeError (var) or ReferenceError (let/const).
Cricket analogy: Like a scheduled fixture announced fully before the season starts (function declaration hoisting), versus a friendly match only confirmed once the invitation is actually sent (function expression) — showing up early throws you out.
What does `{} == {}` and `{} === {}` evaluate to, and why?
Both evaluate to false. Objects (including arrays) are compared by reference, not by structural value, for both == and ===. Two separately created objects are never equal even if their contents are identical, unless they refer to the exact same object in memory.
Cricket analogy: Like two identical scorecards from different matches never being 'the same match' no matter how alike their numbers look — only two scorers holding the literal same physical card would count as equal.
Trace the execution order of an async function using await inside a try-catch.
javascript
async function run() {
console.log('start');
try {
await Promise.reject(new Error('fail'));
} catch (err) {
console.log('caught:', err.message);
}
console.log('end');
}
run();
console.log('after call');
Output: start, after call, caught: fail, end. The async function runs synchronously until the first await, printing start. Control then returns to the caller, which logs after call. Once the rejected promise settles (on a microtask), execution resumes inside the catch block, printing caught: fail, then end`.
Cricket analogy: Like an umpire announcing 'play' (start), the crowd cheering immediately (after call), then once the DRS review comes back overturned, announcing 'given not out on review' (caught), followed by 'resume play' (end).
What does the spread operator produce when merging two objects with overlapping keys?
javascript
const a = { x: 1, y: 2 };
const b = { y: 5, z: 3 };
console.log({ ...a, ...b });
Output: { x: 1, y: 5, z: 3 }. Spread copies own enumerable properties in order; later sources overwrite matching keys from earlier ones, so b's y: 5 wins over a's y: 2`.
Cricket analogy: Like merging a batter's base stats {avg:1, strikeRate:2} with a recent-form update {strikeRate:5, boundaries:3} — the recent strike rate 5 overwrites the old 2, yielding {avg:1, strikeRate:5, boundaries:3}.
Classic closure-in-a-loop question: what fixes the 'all callbacks log the same value' bug?
The bug occurs when a loop uses var and schedules a closure (e.g. a setTimeout callback) inside it — all closures share one i binding that ends at its final value. Fixes: (1) replace var with let, giving each iteration its own binding; or (2) wrap the loop body in an IIFE that takes i as a parameter, creating a new scope per iteration; or (3) use Array.prototype.forEach, whose callback parameter is a fresh binding per call.
Cricket analogy: Like every fielder's delayed relay throw referencing the shared 'current over' scoreboard so they all report the final over number — fixed by giving each fielder a private notecard (let), a huddle before each throw (IIFE), or a rotation handing out one fresh card per fielder (forEach).
What is the difference between a shallow copy and a deep copy, with an example?
javascript
const original = { a: 1, nested: { b: 2 } };
const shallow = { ...original };
shallow.nested.b = 99;
console.log(original.nested.b);
Output: 99. Spread (and Object.assign) only copies top-level properties; nested objects are still shared by reference, so mutating shallow.nested.b also changes original.nested.b. A true deep copy (e.g. structuredClone(original)) would copy nested objects recursively, leaving original` untouched.
Cricket analogy: Like copying a team's full roster sheet but the 'injury list' sub-document is still the same shared file — updating an injury on the copy also updates the original team's shared injury record, since only the top-level sheet was duplicated.
What does comparing NaN with itself return, and how do you correctly test for NaN?
NaN === NaN evaluates to false — NaN is the only value in JavaScript that is not equal to itself, by IEEE 754 specification. The correct way to test for it is Number.isNaN(value), which checks strictly without type coercion (unlike the legacy global isNaN(), which coerces its argument to a number first, e.g. isNaN('foo') is true).
Cricket analogy: Like a scorer's 'no result' entry never matching another 'no result' entry even in the same match — you must use a dedicated washed-out check rather than comparing directly, since a loose isNaN-style guess coerces anything unusual to true.
3. Quick Reference
varin a loop shares one binding across all closures;letcreates a new binding per iteration.typeof null === 'object'; useArray.isArray()to detect arrays, nottypeof.- Objects/arrays are always compared by reference with
==and===, never by structural equality. - Function declarations are fully hoisted; function expressions/arrow functions are not.
- Spread/
Object.assignproduce shallow copies — nested objects remain shared references. NaN === NaNisfalse; useNumber.isNaN()to test for NaN reliably.
4. Key Takeaways
- Exam questions favor precise output-tracing over recalling definitions — practice reading code line by line.
- The classic 'var in a loop with setTimeout' question tests understanding of scope and closures simultaneously.
- Reference equality for objects/arrays is one of the most commonly mis-answered exam topics.
- Async/await questions require tracking exactly when control returns to the caller versus when it resumes after await.
- Shallow vs deep copy distinctions matter whenever spread syntax or Object.assign is used on nested data.
- Always verify numeric edge cases like NaN comparisons explicitly rather than assuming intuitive behavior.
Practice what you learned
1. In `for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); }`, what gets printed?
2. What does `[1,2,3,4,5].map(n => n*2).filter(n => n>4).reduce((s,n)=>s+n,0)` evaluate to?
3. What does `{} === {}` evaluate to?
4. Calling a function expression before its assignment line (declared with const) results in:
5. Given `const a={x:1,y:2}, b={y:5,z:3}; console.log({...a,...b});`, what is logged?
6. What does `NaN === NaN` evaluate to?
7. After `const shallow = {...original}` where original has a nested object, mutating `shallow.nested.b` will:
8. In an async function, what runs immediately when the function is called, up until the first await?
Was this page helpful?
You May Also Like
Common JavaScript Interview Questions
A curated set of frequently asked JavaScript interview questions covering fundamentals, closures, the event loop, and ES6+ features.
Closures in JavaScript
Understand how closures let inner functions remember and access variables from their outer scope, and how loop variable capture can trip you up.
let, const and Block Scope in JavaScript
How let and const introduced block scoping, the temporal dead zone, and why const does not make objects immutable.
async/await in JavaScript
Learn how async/await provides synchronous-looking syntax for working with Promises, including error handling with try/catch.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics