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

瀏覽器相容性

另見