Writing a Polyfill of the Array reduce() Method in JavaScript
Writing polyfills is one of the best ways to understand how JavaScript built-ins really work. Today we'll implement Array.prototype.reduce from scratch—edge cases and all.
How reduce() Works
reduce() iterates through an array, calling a callback function on each element and passing the result (the accumulator) forward to the next call. The final accumulated value is returned.
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, cur) => acc + cur, 0);
// => 10The callback receives four arguments:
- accumulator — the running result
- currentValue — the current element
- currentIndex — the index of the current element
- array — the original array
The Polyfill
Array.prototype.myReduce = function (callback, initialValue) {
// 'this' refers to the array the method is called on
const array = this;
let accumulator;
let startIndex;
if (arguments.length >= 2) {
// Initial value was provided
accumulator = initialValue;
startIndex = 0;
} else {
// No initial value — use first element, skip it in iteration
if (array.length === 0) {
throw new TypeError(
"Reduce of empty array with no initial value"
);
}
accumulator = array[0];
startIndex = 1;
}
for (let i = startIndex; i < array.length; i++) {
accumulator = callback(accumulator, array[i], i, array);
}
return accumulator;
};Edge Cases Handled
Empty array without an initial value
The native reduce() throws a TypeError in this case. Our polyfill mirrors that behaviour:
[].myReduce((acc, cur) => acc + cur);
// TypeError: Reduce of empty array with no initial valueSingle element, no initial value
The element itself is returned without calling the callback at all:
[42].myReduce((acc, cur) => acc + cur);
// => 42Practical Uses
Sum
[1, 2, 3].myReduce((acc, n) => acc + n, 0); // 6Flatten one level
[[1, 2], [3, 4]].myReduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4]Count occurrences
["a", "b", "a", "c", "b", "a"].myReduce((acc, letter) => {
acc[letter] = (acc[letter] ?? 0) + 1;
return acc;
}, {});
// { a: 3, b: 2, c: 1 }Group by property
const people = [
{ name: "Alice", dept: "Engineering" },
{ name: "Bob", dept: "Design" },
{ name: "Carol", dept: "Engineering" },
];
people.myReduce((acc, person) => {
(acc[person.dept] = acc[person.dept] ?? []).push(person.name);
return acc;
}, {});
// { Engineering: ["Alice", "Carol"], Design: ["Bob"] }Understanding reduce() at this level makes every other higher-order array method easier to reason about. Once you can write the polyfill, you can implement map, filter, and find on top of it—they're all just reduce in disguise.