Array.map
Array.prototype.map 用于把数组中的每个元素通过回调函数“映射”为一个新值,并返回一个等长的新数组。
const result = arr.map((value, index, array) => {
return newValue;
}, thisArg);一句话理解
map不会修改原数组(除非你的回调里主动改)- 返回的新数组长度与原数组
length相同 - “空位(holes)”不会调用回调,结果里也保持空位
语义细节(高频)
1) 回调参数
value:当前元素值index:当前索引array:原数组引用
2) thisArg
map(callback, thisArg) 的第二个参数会作为回调内部的 this。
3) holes(稀疏数组)行为
const a = [ , 1, , 2];
const b = a.map((x) => x * 10);
// b 仍然在 0、2 位置是空位,不会变成 undefined4) 遍历时机(长度快照)
规范上会先读取 len = ToLength(O.length),因此遍历次数由开始时的 length 决定。
回调过程中你修改数组内容可能影响后续取值,但不会改变本次循环的上限 len。
手写实现思路
写一个“尽量贴近原生语义”的 polyfill,需要注意:
this可能不是数组(比如类数组对象),要ToObject(this)- 要处理
this == null(null/undefined)时报错 - 需要支持
thisArg - 需要跳过 holes(用
k in O判断) - 返回数组长度应为
len
实现:map(贴近原生)
export function map<T, U>(
arrayLike: ArrayLike<T>,
callback: (value: T, index: number, array: ArrayLike<T>) => U,
thisArg?: any
): U[] {
if (arrayLike == null) {
throw new TypeError('Cannot read properties of null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(`${callback} is not a function`);
}
const O = Object(arrayLike) as any;
/**
* 右移 0 位,数值不变,但强制触发 ToUint32 转换:
把 O.length 转成范围在 [0, 2^32-1] 的整数
也会把 NaN、undefined、负数等变成合理的 0 或对应值
*/
const len = (O.length >>> 0); // ToLength 的常见近似写法
const A = new Array<U>(len);
for (let k = 0; k < len; k++) {
if (k in O) {
A[k] = callback.call(thisArg, O[k], k, O);
}
// else: 保持空位
}
return A;
}示例
const arr = Array.from({ length: 5 }, (_, i) => i);
const squared = map2(arr, (x) => x * x);
// [0, 1, 4, 9, 16]复杂度
- 时间:
- 空间:(返回新数组)
常见坑点
- 用
push会把 holes “压缩掉”,和原生行为不一致;贴近原生要按索引赋值并保留空位。 - 不要用
for...of直接遍历:它会跳过 holes 的方式与map语义不同,且不利于保留空位。 - 回调里修改数组会影响后续读取的值,但循环上限仍是开始时的
len。 ```