map() is an array method that creates a new array populated with the results of calling a provided function on every element in the calling array.
// `thisArg` is optional; when specified, it provides a value for
// `this` when `callbackFn` is executing
map(callbackFn, thisArg)
map() preserves the same index range and transforms only existing values. The output has the same length and the same holes as the input, but the callback result replaces each existing element.
Array.prototype.myMap = function (callbackFn, thisArg) {
// We use this to determine how many indexes we need to inspect.
const len = this.length;
// This is important because map() always returns a new array.
// Using new Array(len) also preserves holes in sparse arrays.
const array = new Array(len);
// We check every index because the original array may contain holes.
for (let k = 0; k < len; k++) {
// A sparse array can have a missing index (a "hole").
// `k in this` allows us to skip those holes instead of calling
// the callback with undefined.
if (k in this) {
// this[k] → current element
// k → current index
// this → original array
// callbackFn.call(thisArg, ...) allows the caller to control
// what `this` refers to inside the callback.
// The value returned by the callback is stored at the same
// index in the new array.
array[k] = callbackFn.call(
thisArg,
this[k],
k,
this
);
}
}
return array;
};
For the spec solution, a specification-oriented version based on Array.prototype.map looks like this.
Array.prototype.myMap = function (callbackFn, thisArg) {
// This allows myMap to work with array-like objects and handles
// primitive values consistently with the specification.
const O = Object(this);
// `>>> 0` converts the length into an unsigned 32-bit integer,
// giving us the number of indexes we need to process.
const len = O.length >>> 0;
// Native map() throws a TypeError when the callback is not a function.
if (typeof callbackFn !== "function") {
throw new TypeError("this is not a function");
}
// map() does not modify the original array.
// Creating the array with `len` also preserves holes in sparse arrays.
let array = new Array(len);
let k = 0;
while (k < len) {
// Object properties are ultimately accessed using property keys,
// which are strings (or symbols).
let presentKey = String(k);
// This is important for sparse arrays.
// A hole means the property does not exist, so map() skips it.
// `in` also checks the prototype chain, which matches the
// specification's HasProperty operation.
if (k in O) {
let kValue = O[k];
// - kValue → current element
// - k → current index
// - O → original array/object
// `call()` allows us to provide the `thisArg` as the `this`
// value inside the callback.
let mappedValue = callbackFn.call(
thisArg,
kValue,
k,
O
);
// Instead of simply doing:
// array[k] = mappedValue;
// the specification uses CreateDataProperty, which is
// conceptually represented here using Object.defineProperty().
// This explicitly creates an ordinary data property with:
// value → the value returned by the callback
// writable → the property can be changed later
// enumerable → the property appears during enumeration
// configurable → the property can be deleted or reconfigured
// Most importantly, we only create this property when `k in O`
// is true. If the original array has a hole at this index,
// this block is never executed, so the result keeps the hole.
Object.defineProperty(array, presentKey, {
value: mappedValue,
writable: true,
enumerable: true,
configurable: true,
});
}
k = k + 1;
}
return array;
};
Top comments (0)