Implement a polyfill for Array.prototype.reduce
Iterate the array applying the callback with an accumulator. If initial value supplied, start there; else use the first defined element as initial and start from index 1. Skip holes (sparse). Throw TypeError on empty array with no initial. Pass (acc, cur, i, arr) signature.
Implementation
Array.prototype.myReduce = function (callback, initialValue) {
if (this == null) throw new TypeError("array is null or undefined");
if (typeof callback !== "function") throw new TypeError("callback is not a function");
const arr = Object(this);
const len = arr.length >>> 0; // normalize to uint32
let i = 0;
let acc;
if (arguments.length >= 2) {
acc = initialValue;
} else {
// Find first defined index (skip holes in sparse arrays)
while (i < len && !(i in arr)) i++;
if (i >= len) throw new TypeError("Reduce of empty array with no initial value");
acc = arr[i++];
}
for (; i < len; i++) {
if (i in arr) { // skip holes
acc = callback(acc, arr[i], i, arr);
}
}
return acc;
};Walk-through
this == nullthrows (called on null/undefined receiver).Object(this)coerces in case someone calls on a primitive.length >>> 0is the spec way to normalize length to a 32-bit unsigned int.- With initial value: start accumulating from index 0.
- Without: use the first present element as accumulator, start from the next index. Throw if no elements.
i in arrdistinguishes holes fromundefined— sparse arrays skip holes.- Callback receives
(accumulator, currentValue, index, array).
Tests
[1, 2, 3, 4].myReduce((a, c) => a + c); // 10 (no initial)
[1, 2, 3, 4].myReduce((a, c) => a + c, 100); // 110 (with initial)
[].myReduce((a, c) => a + c); // TypeError
[].myReduce((a, c) => a + c, 0); // 0
[1, , 3].myReduce((a, c) => a + c); // 4 (skips hole)Edge cases
- Empty array, no initial → TypeError (matches spec).
- Empty array, with initial → returns initial.
- Sparse arrays →
forEach/reduceboth skip holes;for..ofdoesn't. - Receiver coercion — called on array-like:
Array.prototype.myReduce.call("abc", ...)works.
Why interviewers ask
Tests handling of: signature semantics, the no-initial-value branch, edge cases (empty + sparse), and proper error reporting. It's also a foundation for understanding reduceRight, transduce, etc.
Interview framing
"Initial value is optional — if provided, accumulate from index 0; if not, use the first present element and start from the next index, throwing on a completely empty array. Skip holes via i in arr (forEach and reduce both do this). Normalize length with length >>> 0 per spec. Callback signature is (acc, cur, i, arr). The empty-array-no-initial case throwing TypeError is the spec behavior most polyfills forget."
Follow-up questions
- •Implement reduceRight.
- •What's the difference between hole and undefined in an array?
- •How does length >>> 0 work?
Common mistakes
- •Forgetting the no-initial-value branch.
- •Not throwing on empty array without initial.
- •Treating undefined as a hole.
Performance considerations
- •O(n). Hole-check (`i in arr`) is slow on huge arrays — native reduce uses optimized internal iteration.
Edge cases
- •Empty + no initial → TypeError.
- •Sparse arrays.
- •Receiver is a string / array-like.
Real-world examples
- •Polyfill libraries (core-js), pre-IE9 environments, MDN reference impls.