polyfills for array methods
Polyfills for Array Methods
Mastering prototype inheritance and edge-case handling by implementing map, filter, and reduce from scratch.
The Universal Electrical Adapter
Writing array polyfills is one of the most common senior frontend interview tests. It tests your mastery of `Array.prototype`, the dynamic `this` keyword, callback arguments, and edge-case defensiveness.
Feature Detection Guard
Always check `if (!Array.prototype.customMethod)` before defining. Never overwrite native implementations which are heavily optimized at the C++ level.
Binding to the Caller with `this`
Inside `Array.prototype.myMethod = function(...)`, `this` references the array instance executing the call. We must validate that `this != null`.
Validating Callback Functions
If the caller passes a non-function as the callback, throw `new TypeError(callback + " is not a function")` to match ECMAScript spec.
Handling Holes in Sparse Arrays
Native methods skip missing indices. Using `i in this` or `Object.hasOwn(this, i)` ensures unassigned indexes are not passed to the callback.
Handling reduce() initialValue Semantics
If `initialValue` is provided, accumulator starts with it and index starts at 0. If omitted, accumulator takes `this[0]` and index starts at 1.
Production-grade polyfills for myMap and myReduce:
// Polyfill for Array.prototype.map
Array.prototype.myMap = function(callback, thisArg) {
if (this == null) throw new TypeError("Cannot iterate over null or undefined");
if (typeof callback !== "function") throw new TypeError(callback + " is not a function");
const O = Object(this);
const len = O.length >>> 0; // Unsigned 32-bit int conversion
const result = new Array(len);
for (let i = 0; i < len; i++) {
if (i in O) {
result[i] = callback.call(thisArg, O[i], i, O);
}
}
return result;
};
// Polyfill for Array.prototype.reduce
Array.prototype.myReduce = function(callback, initialValue) {
if (this == null) throw new TypeError("Cannot reduce null or undefined");
if (typeof callback !== "function") throw new TypeError(callback + " is not a function");
const O = Object(this);
const len = O.length >>> 0;
let k = 0;
let accumulator;
if (arguments.length >= 2) {
accumulator = initialValue;
} else {
// Find first present index if initialValue is omitted
let kPresent = false;
while (k < len && !kPresent) {
if (k in O) {
accumulator = O[k];
kPresent = true;
}
k++;
}
if (!kPresent) throw new TypeError("Reduce of empty array with no initial value");
}
for (; k < len; k++) {
if (k in O) {
accumulator = callback(accumulator, O[k], k, O);
}
}
return accumulator;
};
// Test
const nums = [1, 2, 3];
console.log(nums.myMap(x => x * 2));
console.log(nums.myReduce((acc, x) => acc + x, 0));
[ 2, 4, 6 ]
6
Calling reduce on Empty Array with No Initial Value
The Risk: `[].reduce((a, b) => a + b)` throws `TypeError: Reduce of empty array with no initial value` in ECMAScript spec.
The Fix: Ensure your polyfill checks for `arguments.length >= 2`, and if empty with no initial value, throws a TypeError.
Using Arrow Functions for Polyfill Implementations
The Risk: Declaring `Array.prototype.myMap = (callback) => { ... }` binds `this` lexically to the enclosing scope (window/global), breaking access to the array instance.
The Fix: Always use traditional `function(...)` declarations so `this` dynamically refers to the calling array.
- ✓ Polyfills bridge language gaps across browser versions.
- ✓ Never use arrow functions on prototypes (`this` will point to window/global).
- ✓ `this` inside the method is the array instance.
- ✓ In `reduce()`, check `arguments.length >= 2` to distinguish explicit `undefined` from omitted initial value.
- ✓ Skip empty slots in sparse arrays using `if (i in this)`.