Day 76: Review JS Engine Edge Cases & Tricky Concepts
Drill the specific edge cases that separate a confident answer from a shaky one across execution context, closures, the event loop, and prototypes.
Study
Concepts
The recurring "gotcha" list worth memorizing precisely
Revisit these exact edge cases and be able to explain WHY, not just WHAT: `typeof null === 'object'` (a decades-old engine bug preserved for compatibility); `[] + []` is `''` but `[] + {}` is `'[object Object]'` (both operands coerce to strings via valueOf/toString, and array-to-string joins with commas on an empty array producing an empty string); `NaN !== NaN` (per IEEE 754, NaN is defined to never equal itself — use `Number.isNaN` or `Object.is` to check for it correctly); closures capturing a `var` loop variable by reference share ONE binding (Day 2) while `let` creates one binding per iteration.
Also drill: `async function` always wraps its return value in a Promise even if you return a plain value; a `.then()` handler that returns nothing resolves the chain with `undefined`; and `Promise.all` rejects as soon as ANY promise rejects (short-circuits) while `Promise.allSettled` always waits for every promise and never rejects itself — a very commonly confused pair.
See It
Visualizations
Visualization
Promise.all vs Promise.allSettled vs Promise.race vs Promise.any
| Short-circuits on first... | Resolves/rejects with... | |
|---|---|---|
| Promise.all | first REJECTION | array of values, or the first rejection reason |
| Promise.allSettled | never short-circuits | array of {status, value|reason} for every input |
| Promise.race | first SETTLEMENT (resolve OR reject) | that single result |
| Promise.any | first FULFILLMENT | that value, or an AggregateError if ALL reject |
Build It
Code Examples
A rapid-fire self-check set — predict each before running
console.log(typeof null); // 'object' (legacy engine quirk)
console.log([] + []); // ''
console.log([] + {}); // '[object Object]'
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
console.log(0.1 + 0.2 === 0.3); // false (floating point precision)
async function f() { return 42; }
f().then(console.log); // 42 — auto-wrapped in a Promise
Promise.all([Promise.resolve(1), Promise.reject('err'), Promise.resolve(3)])
.catch((e) => console.log('all rejected with:', e)); // 'err' — short-circuits
Promise.allSettled([Promise.resolve(1), Promise.reject('err')])
.then((r) => console.log(r));
// [{status:'fulfilled', value:1}, {status:'rejected', reason:'err'}]Remember
Key Takeaways
- typeof null, NaN !== NaN, and array/object-to-string coercion quirks are classic interview rapid-fire questions — know the WHY behind each.
- An async function always returns a Promise, even for a plain returned value — a very common source of confusion for less experienced candidates.
- Promise.all short-circuits on first rejection; Promise.allSettled never does and never itself rejects — know exactly when to use each.
- Floating point precision (0.1 + 0.2 !== 0.3) is a real, not just trivia, concern in any code comparing computed decimal values.
- This day is deliberately review, not new material — the goal is speed and confidence, not first-time learning.
Do It
Practice
- 1Run the full rapid-fire snippet above cold, predicting every line before executing, and note any you got wrong.
- 2Write your own 5-question rapid-fire quiz covering closures, this, and prototypes (Week 1-2) and answer it cold 24 hours later.
- 3Explain Promise.any vs Promise.race out loud in one sentence each, without looking at notes.