Blog icon indicating copy to clipboard operation
Blog copied to clipboard

[MDN] Map

Open ChaoLiou opened this issue 5 years ago • 0 comments

Map

  • Map 物件是 key-value 配對, 而且記得原本插入 keys 的順序. key 或 value 可以使用任何值(包括物件和原生值).

說明

  • Map 物件會以插入的順序 iterate 項目 - for...of 每次回傳 [key, value] 的陣列.

Key 的相等性

  • 相等性是根據 sameValueZero 演算法.
  • NaN 跟其他 NaN 相等(雖然 NaN !== NaN), 其他值都會以 === 運算子的語義決定是否相等.
  • 目前的 ECMAScript 規格中, -0+0 是相等的.

物件 vs. Maps

  • 物件與 Map 相似 - 兩者都可以
    • 設定 keys 和 values
    • 取回那些 values
    • 刪除 keys
    • 偵測 key 是否有儲存值
Map Object
意外出現的 keys 預設不含任何 keys. 只會有明確放入的配對 本身有 prototype, 所以含預設的 keys, 如果不注意可能會有衝突
key 類型 任何值(包括 functions, objects 或任何原生值) 必須是 String 或 Symbol
key 順序 以插入的順序 iterate entries, keys 和 values. 雖然現在都有排序, 但以前沒有, 而且順序很複雜. 所以, 最好不要依賴屬性順序
大小 數量可以從 size 屬性取得 手動取得數量
Iteration iterable, 所以可直接 iterate 沒有實作 iteration protocol, 無法直接使用 JS for...of
(可以用 Object.keys/Object.entries)
(如果是物件的屬性是 enumerable 可以用 for...in)
效能 適合頻繁地新增刪除配對會 不適合頻繁地新增刪除配對

設定物件屬性

// X
let wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
// 這樣做不會動到 Map 的資料結構. 他用的是一般物件的功能.

wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
  • 正確使用是透過 set(key, value) 方法儲存資料
let 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

範例

使用

let myMap = new Map();

let keyString = "a string";
let keyObj = {};
let keyFunc = function () {};

// 設值
myMap.set(keyString, "是一個字串");
myMap.set(keyObj, "是一個 keyObj");
myMap.set(keyFunc, "是一個 keyFunc");

myMap.size; // 3

// 取值
myMap.get(keyString); // "是一個字串"
myMap.get(keyObj); // "是一個 keyObj"
myMap.get(keyFunc); // "是一個 keyFunc"

myMap.get("a string"); // "是一個字串"
// 因為 keyString === 'a string'
myMap.get({}); // undefined, 因為 keyObj !== {}
myMap.get(function () {}); // undefined, 因為 keyFunc !== function () {}

用 NaN 當作 Map keys

  • NaN 也可以用作 key. 就算每個 NaN 都和本身不相等(NaN !== NaN), 以下的範例都會正常, 因為 NaN 彼此難以區分:
let myMap = new Map();
myMap.set(NaN, "not a number");

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

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

for..of

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

for (let [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one

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

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

for (let [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one

forEach()

myMap.forEach(function (value, key) {
  console.log(key + " = " + value);
});
// 0 = zero
// 1 = one

與陣列物件的關係

let kvArray = [
  ["key1", "value1"],
  ["key2", "value2"],
];

// 用正規的 Map constructor 轉換 2維 key-value 陣列
let myMap = new Map(kvArray);

myMap.get("key1"); // 回傳 "value1"

// 用 Array.from() 轉回 2D key-value 陣列
console.log(Array.from(myMap)); // 這個陣列會跟 kvArray 一樣

// 使用 spread 語法
console.log([...myMap]);

// 或使用 keys() / values() iterators, 轉成陣列
console.log(Array.from(myMap.keys())); // ["key1", "key2"]

複製與合併

  • 複製 Map
let original = new Map([[1, "one"]]);

let clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false (淺層比較)

注意: 資料本身沒有複製

  • Maps 可以合併, 但保持 key 的唯一性
let first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

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

// 合併兩個 maps. 後者重複的 key 覆蓋前者.
// Spread 運算子本質上將 Map 轉成陣列
let merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
  • Maps 也可以和陣列合併
let first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

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

// 用陣列合併 maps. 後者重複的 key 覆蓋前者.
let merged = new Map([...first, ...second, [1, "eins"]]);

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

瀏覽器相容

  • IE 部分支援, 手持 Opera Android 不支援 @@toStringTag, 其他瀏覽器都支援

ChaoLiou avatar Jan 28 '21 03:01 ChaoLiou