Array.map

10 小时前
/ ,
3

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 位置是空位,不会变成 undefined

4) 遍历时机(长度快照)

规范上会先读取 len = ToLength(O.length),因此遍历次数由开始时的 length 决定。 回调过程中你修改数组内容可能影响后续取值,但不会改变本次循环的上限 len

手写实现思路

写一个“尽量贴近原生语义”的 polyfill,需要注意:

  • this 可能不是数组(比如类数组对象),要 ToObject(this)
  • 要处理 this == nullnull/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]

复杂度

  • 时间:O(n)O(n)
  • 空间:O(n)O(n)(返回新数组)

常见坑点

  • push 会把 holes “压缩掉”,和原生行为不一致;贴近原生要按索引赋值并保留空位。
  • 不要用 for...of 直接遍历:它会跳过 holes 的方式与 map 语义不同,且不利于保留空位。
  • 回调里修改数组会影响后续读取的值,但循环上限仍是开始时的 len。 ```

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...