Array.prototype.map()

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

map() 方法的 Array 实例会在调用数组中的每个元素上执行一个提供的函数,并创建一个新数组,新数组中的元素是函数调用的返回值。

试一试

const array = [1, 4, 9, 16];

// Pass a function to map
const mapped = array.map((x) => x * 2);

console.log(mapped);
// Expected output: Array [2, 8, 18, 32]

语法

js
map(callbackFn)
map(callbackFn, thisArg)

参数

callbackFn

为数组中的每个元素执行的函数。它的返回值将作为新数组中的一个元素。该函数以以下参数调用:

element

数组中正在处理的当前元素。

index

数组中正在处理的当前元素的索引。

array

调用 map() 的数组。

thisArg 可选

在执行 callbackFn 时用作 this 的值。请参阅 迭代方法

返回值

一个新数组,其中每个元素都是回调函数的结果。

描述

map() 方法是一个迭代方法。它对数组中的每个元素调用一次提供的 callbackFn 函数,并根据结果构建一个新数组。有关这些方法通常如何工作的更多信息,请阅读迭代方法部分。

callbackFn 仅对具有已赋值的数组索引调用。对于稀疏数组中的空槽,它不会被调用。

map() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。

由于 map 构建一个新数组,调用它而不使用返回的数组是一种反模式;请改用 forEachfor...of

示例

将数字数组映射到平方根数组

以下代码将一个数字数组转换为一个新数组,新数组包含第一个数组中数字的平方根。

js
const numbers = [1, 4, 9];
const roots = numbers.map((num) => Math.sqrt(num));

// roots is now     [1, 2, 3]
// numbers is still [1, 4, 9]

使用 map 重新格式化数组中的对象

以下代码将一个对象数组转换为一个新数组,新数组包含重新格式化后的对象。

js
const kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 },
];

const reformattedArray = kvArray.map(({ key, value }) => ({ [key]: value }));

console.log(reformattedArray); // [{ 1: 10 }, { 2: 20 }, { 3: 30 }]
console.log(kvArray);
// [
//   { key: 1, value: 10 },
//   { key: 2, value: 20 },
//   { key: 3, value: 30 }
// ]

将 parseInt() 与 map() 一起使用

通常使用只有一个参数的回调函数(正在遍历的元素)。某些函数也经常与一个参数一起使用,即使它们接受额外的可选参数。这些习惯可能导致令人困惑的行为。请考虑以下示例:

js
["1", "2", "3"].map(parseInt);

尽管你可能期望结果是 [1, 2, 3],但实际结果是 [1, NaN, NaN]

parseInt 通常只用一个参数调用,但它接受两个参数。第一个参数是表达式,第二个参数是基数。对于回调函数,Array.prototype.map 传递 3 个参数:元素、索引和数组。第三个参数被 parseInt 忽略——但第二个参数不会被忽略!这是可能产生混淆的原因。

以下是一个简洁的迭代步骤示例:

js
/* first iteration  (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/* third iteration  (index is 2): */ parseInt("3", 2); // NaN

为了解决这个问题,可以定义另一个只接受一个参数的函数:

js
["1", "2", "3"].map((str) => parseInt(str, 10)); // [1, 2, 3]

你也可以使用 Number 函数,它只接受一个参数:

js
["1", "2", "3"].map(Number); // [1, 2, 3]

// But unlike parseInt(), Number() will also return a float or (resolved) exponential notation:
["1.1", "2.2e2", "3e300"].map(Number); // [1.1, 220, 3e+300]

// For comparison, if we use parseInt() on the array above:
["1.1", "2.2e2", "3e300"].map((str) => parseInt(str, 10)); // [1, 2, 3]

请参阅 Allen Wirfs-Brock 的 JavaScript 可选参数的危险,以获取更多讨论。

映射的数组包含 undefined

当返回 undefined 或不返回任何内容时,结果数组将包含 undefined。如果你想删除该元素,可以链接一个 filter() 方法,或者使用 flatMap() 方法并返回一个空数组来表示删除。

js
const numbers = [1, 2, 3, 4];
const filteredNumbers = numbers.map((num, index) => {
  if (index < 3) {
    return num;
  }
});

// index goes from 0, so the filterNumbers are 1,2,3 and undefined.
// filteredNumbers is [1, 2, 3, undefined]
// numbers is still [1, 2, 3, 4]

有副作用的映射

回调函数可以有副作用。

js
const cart = [5, 15, 25];
let total = 0;
const withTax = cart.map((cost) => {
  total += cost;
  return cost * 1.2;
});
console.log(withTax); // [6, 18, 30]
console.log(total); // 45

这并不推荐,因为复制方法最好与纯函数一起使用。在这种情况下,我们可以选择迭代数组两次。

js
const cart = [5, 15, 25];
const total = cart.reduce((acc, cost) => acc + cost, 0);
const withTax = cart.map((cost) => cost * 1.2);

有时这种模式会走到极端,而 map() 唯一有用的地方就是引起副作用。

js
const products = [
  { name: "sports car" },
  { name: "laptop" },
  { name: "phone" },
];

products.map((product) => {
  product.price = 100;
});

如前所述,这是一种反模式。如果你不使用 map() 的返回值,请改用 forEach()for...of 循环。

js
products.forEach((product) => {
  product.price = 100;
});

或者,如果你想创建一个新数组,可以这样做:

js
const productsWithPrice = products.map((product) => ({
  ...product,
  price: 100,
}));

使用 callbackFn 的第三个参数

当你想访问数组中的另一个元素时,array 参数非常有用,尤其是在你没有一个引用该数组的现有变量时。以下示例首先使用 filter() 提取正值,然后使用 map() 创建一个新数组,新数组中的每个元素都是其邻居和它本身的平均值。

js
const numbers = [3, -1, 1, 4, 1, 5, 9, 2, 6];
const averaged = numbers
  .filter((num) => num > 0)
  .map((num, idx, arr) => {
    // Without the arr argument, there's no way to easily access the
    // intermediate array without saving it to a variable.
    const prev = arr[idx - 1];
    const next = arr[idx + 1];
    let count = 1;
    let total = num;
    if (prev !== undefined) {
      count++;
      total += prev;
    }
    if (next !== undefined) {
      count++;
      total += next;
    }
    const average = total / count;
    // Keep two decimal places
    return Math.round(average * 100) / 100;
  });
console.log(averaged); // [2, 2.67, 2, 3.33, 5, 5.33, 5.67, 4]

array 参数不是正在构建的数组——无法从回调函数访问正在构建的数组。

在稀疏数组上使用 map()

稀疏数组在 map() 之后仍然是稀疏的。空槽的索引在返回的数组中仍然是空的,并且回调函数不会在它们上面被调用。

js
console.log(
  [1, , 3].map((x, index) => {
    console.log(`Visit ${index}`);
    return x * 2;
  }),
);
// Visit 0
// Visit 2
// [2, empty, 6]

在非数组对象上调用 map()

map() 方法读取 thislength 属性,然后访问键为小于 length 的非负整数的每个属性。

js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
  3: 5, // ignored by map() since length is 3
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2));
// [ 4, 9, 16 ]

此示例演示了如何遍历由 querySelectorAll 收集的对象集合。这是因为 querySelectorAll 返回一个 NodeList(这是一个对象集合)。在这种情况下,我们返回屏幕上所有选定 option 的值。

js
const elems = document.querySelectorAll("select option:checked");
const values = Array.prototype.map.call(elems, ({ value }) => value);

你也可以使用 Array.from()elems 转换为数组,然后访问 map() 方法。

规范

规范
ECMAScript® 2026 语言规范
# sec-array.prototype.map

浏览器兼容性

另见