Map

Baseline 廣泛可用 *

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

* 此特性的某些部分可能存在不同級別的支援。

Map 物件儲存鍵值對,並且能夠記住鍵的原始插入順序。任何值(無論是物件還是原始值)都可以用作鍵或值。

試一試

const map = new Map();

map.set("a", 1);
map.set("b", 2);
map.set("c", 3);

console.log(map.get("a"));
// Expected output: 1

map.set("a", 97);

console.log(map.get("a"));
// Expected output: 97

console.log(map.size);
// Expected output: 3

map.delete("b");

console.log(map.size);
// Expected output: 2

描述

Map 物件是鍵值對的集合。Map 中的鍵只能出現一次;它在 Map 集合中是唯一的。Map 物件按鍵值對進行迭代 — 一個 for...of 迴圈在每次迭代中返回一個包含 [key, value] 的兩元素陣列。迭代按插入順序進行,這對應於每個鍵值對首次透過 set() 方法插入到 Map 中的順序(也就是說,當呼叫 set() 時,Map 中還沒有具有相同值的鍵)。

規範要求 Map 的實現“平均訪問時間應低於集合中元素數量的次線性”。因此,它可以在內部表示為雜湊表(O(1) 查詢)、搜尋樹(O(log(N)) 查詢)或任何其他資料結構,只要其複雜度優於 O(N)。

鍵的相等性

值的相等性基於 SameValueZero 演算法。(它曾經使用 SameValue,該演算法將 0-0 視為不同。請檢視瀏覽器相容性。)這意味著 NaN 被認為是與 NaN 相同的(即使 NaN !== NaN 為 true),所有其他值都根據 === 運算子的語義被視為相等。此外,對於物件鍵,相等性基於物件標識。它們透過引用而非值進行比較。有關示例,請參閱使用 Map 物件

Object 與 Map

Object 類似於 Map——兩者都允許你將鍵設定為值、檢索這些值、刪除鍵以及檢測某個鍵是否儲存了內容。因此(並且因為沒有內建替代方案),Object 歷史上一直被用作 Map

然而,在某些情況下,Map 具有重要的差異,使其更優越:

Map Object
意外的鍵 預設情況下,Map 不包含任何鍵。它只包含顯式放入其中的內容。

Object 有一個原型,因此它包含預設鍵,如果你不小心,這些鍵可能會與你自己的鍵衝突。

注意:這可以透過使用 Object.create(null) 來繞過,但這很少被使用。

安全 Map 可以安全地與使用者提供的鍵和值一起使用。

Object 上設定使用者提供的鍵值對可能會允許攻擊者覆蓋物件的原型,這可能導致物件注入攻擊原型汙染攻擊。像意外部索引鍵問題一樣,這也可以透過使用 null-原型物件來緩解。

鍵型別 Map 的鍵可以是任何值(包括函式、物件或任何原始值)。 Object 的鍵必須是 StringSymbol
鍵的順序

Map 中的鍵以直接的方式排序:Map 物件按條目插入的順序迭代條目、鍵和值。

儘管普通 Object 的鍵現在是有序的,但這並非總是如此,並且順序很複雜。因此,最好不要依賴屬性順序。

順序最初僅在 ECMAScript 2015 中為自有屬性定義;ECMAScript 2020 也為繼承屬性定義了順序。但請注意,沒有單一機制可以迭代物件的所有屬性;各種機制都包含不同的屬性子集。(for-in 只包含可列舉的字串鍵屬性;Object.keys 只包含自有、可列舉的字串鍵屬性;Object.getOwnPropertyNames 包含自有、字串鍵屬性,即使是不可列舉的;Object.getOwnPropertySymbols 對僅 Symbol 鍵屬性執行相同的操作,等等。)

大小

Map 中的專案數量可以輕鬆地透過其 size 屬性檢索。 確定 Object 中的專案數量則更為間接且效率低下。一種常見的方法是透過 Object.keys() 返回的陣列的 length 屬性。
迭代 Map 是一個可迭代物件,因此可以直接迭代。

Object 不實現迭代協議,因此物件不能直接使用 JavaScript 的 for...of 語句進行迭代(預設情況下)。

備註

  • 一個物件可以實現迭代協議,或者你可以使用 Object.keysObject.entries 為物件獲取一個可迭代物件。
  • for...in 語句允許你迭代物件的可列舉屬性。
效能

在涉及頻繁新增和刪除鍵值對的場景中表現更好。

未針對頻繁新增和刪除鍵值對進行最佳化。

序列化和解析

沒有原生支援序列化或解析。

(但你可以透過使用帶有 replacer 引數的 JSON.stringify() 和帶有 reviver 引數的 JSON.parse() 來為 Map 構建自己的序列化和解析支援。請參閱 Stack Overflow 問題 How do you JSON.stringify an ES6 Map?)。

使用 JSON.stringify()Object 到 JSON 的原生序列化支援。

使用 JSON.parse() 從 JSON 到 Object 的原生解析支援。

設定物件屬性

設定物件屬性也適用於 Map 物件,並且可能導致相當大的混淆。

因此,這看起來是可行的:

js
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";

console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

但是,這種設定屬性的方式不會與 Map 資料結構互動。它使用的是通用物件的特性。’bla’ 的值不會儲存在 Map 中進行查詢。對資料的其他操作將失敗。

js
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

將資料儲存在 Map 中的正確用法是透過 set(key, value) 方法。

js
const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1

類似 Map 的瀏覽器 API

瀏覽器中類似 Map 的物件(或“類 Map 物件”)是 Web API 介面,它們在許多方面行為類似於 Map

就像 Map 一樣,條目可以按照它們新增到物件的相同順序進行迭代。類似 Map 的物件和 Map 也具有相同名稱和行為的屬性和方法。但是,與 Map 不同的是,它們只允許為每個條目的鍵和值使用特定的預定義型別。

允許的型別在規範 IDL 定義中設定。例如,RTCStatsReport 是一個類似 Map 的物件,它必須使用字串作為鍵,物件作為值。這在下面的規範 IDL 中定義:

webidl
interface RTCStatsReport {
  readonly maplike<DOMString, object>;
};

類 Map 物件是隻讀或讀寫(參見上面 IDL 中的 readonly 關鍵字)。

這些方法和屬性與 Map 中對應的實體具有相同的行為,除了鍵和值型別的限制。

以下是隻讀的類 Map 瀏覽器物件的示例:

建構函式

Map()

建立一個新的 Map 物件。

靜態屬性

Map[Symbol.species]

用於建立派生物件的建構函式。

靜態方法

Map.groupBy()

使用提供的回撥函式返回的值對給定可迭代物件的元素進行分組。最終返回的 Map 使用測試函式中的唯一值作為鍵,這些鍵可用於獲取每個組中的元素陣列。

例項屬性

這些屬性在 Map.prototype 上定義,並由所有 Map 例項共享。

Map.prototype.constructor

建立例項物件的建構函式。對於 Map 例項,初始值是 Map 建構函式。

Map.prototype.size

返回 Map 物件中的鍵/值對的數量。

Map.prototype[Symbol.toStringTag]

[Symbol.toStringTag] 屬性的初始值是字串 "Map"。此屬性用於 Object.prototype.toString()

例項方法

Map.prototype.clear()

Map 物件中移除所有鍵值對。

Map.prototype.delete()

從此 Map 中移除由鍵指定的條目。

Map.prototype.entries()

返回一個新的迭代器物件,其中包含 Map 物件中每個元素的 [key, value] 兩元素陣列,按插入順序排列。

Map.prototype.forEach()

Map 物件中存在的每個鍵值對呼叫 callbackFn 一次,按插入順序。如果向 forEach 提供了 thisArg 引數,它將用作每次回撥的 this 值。

Map.prototype.get()

返回此 Map 中與鍵對應的值,如果沒有則返回 undefined

Map.prototype.getOrInsert() 實驗性

返回此 Map 中與指定鍵對應的值。如果鍵不存在,它將插入一個新條目,其中包含鍵和給定的預設值,並返回插入的值。

Map.prototype.getOrInsertComputed() 實驗性

返回此 Map 中與指定鍵對應的值。如果鍵不存在,它將插入一個新條目,其中包含鍵和從給定回撥計算的預設值,並返回插入的值。

Map.prototype.has()

返回一個布林值,指示此 Map 中是否存在具有指定鍵的條目。

Map.prototype.keys()

返回一個新的迭代器物件,其中包含 Map 物件中每個元素的鍵,按插入順序排列。

Map.prototype.set()

向此 Map 新增一個具有指定鍵和值的新條目,如果鍵已存在,則更新現有條目。

Map.prototype.values()

返回一個新的迭代器物件,其中包含 Map 物件中每個元素的值,按插入順序排列。

Map.prototype[Symbol.iterator]()

返回一個新的迭代器物件,其中包含 Map 物件中每個元素的 [key, value] 兩元素陣列,按插入順序排列。

示例

使用 Map 物件

js
const myMap = new Map();

const keyString = "a string";
const keyObj = {};
const keyFunc = () => {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

console.log(myMap.size); // 3

// getting the values
console.log(myMap.get(keyString)); // "value associated with 'a string'"
console.log(myMap.get(keyObj)); // "value associated with keyObj"
console.log(myMap.get(keyFunc)); // "value associated with keyFunc"

console.log(myMap.get("a string")); // "value associated with 'a string'", because keyString === 'a string'
console.log(myMap.get({})); // undefined, because keyObj !== {}
console.log(myMap.get(() => {})); // undefined, because keyFunc !== () => {}

將 NaN 用作 Map 鍵

NaN 也可以用作鍵。儘管每個 NaN 都不等於它自己(NaN !== NaN 為 true),但以下示例仍然有效,因為 NaNs 彼此之間無法區分:

js
const myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN);
// "not a number"

const otherNaN = Number("foo");
myMap.get(otherNaN);
// "not a number"

使用 for...of 迭代 Map

可以使用 for...of 迴圈迭代 Map:

js
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");

for (const [key, value] of myMap) {
  console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one

for (const key of myMap.keys()) {
  console.log(key);
}
// 0
// 1

for (const value of myMap.values()) {
  console.log(value);
}
// zero
// one

for (const [key, value] of myMap.entries()) {
  console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one

使用 forEach() 迭代 Map

可以使用 forEach() 方法迭代 Map:

js
myMap.forEach((value, key) => {
  console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one

與 Array 物件的關聯

js
const kvArray = [
  ["key1", "value1"],
  ["key2", "value2"],
];

// Use the regular Map constructor to transform a 2D key-value Array into a map
const myMap = new Map(kvArray);

console.log(myMap.get("key1")); // "value1"

// Use Array.from() to transform a map into a 2D key-value Array
console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray

// A succinct way to do the same, using the spread syntax
console.log([...myMap]);

// Or use the keys() or values() iterators, and convert them to an array
console.log(Array.from(myMap.keys())); // ["key1", "key2"]

克隆和合並 Map

就像 Array 一樣,Map 也可以被克隆:

js
const original = new Map([[1, "one"]]);

const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false (useful for shallow comparison)

注意:請記住,資料本身不會被克隆。換句話說,這只是 Map淺複製

Map 可以合併,同時保持鍵的唯一性:

js
const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// Merge two maps. The last repeated key wins.
// Spread syntax essentially converts a Map to an Array
const merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

Map 也可以與數組合並:

js
const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// Merge maps with an array. The last repeated key wins.
const merged = new Map([...first, ...second, [1, "un"]]);

console.log(merged.get(1)); // un
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

規範

規範
ECMAScript® 2026 語言規範
# sec-map-objects

瀏覽器相容性

另見