ES6之Map資料結構

獨孤求贊發表於2020-09-28

1.Map

含義
JavaScript 的物件(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字串當作鍵。這給它的使用帶來了很大的限制。
為了解決這個問題,ES6 提供了 Map 資料結構。它類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。也就是說,Object 結構提供了“字串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的資料結構,Map 比 Object 更合適。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

事實上,不僅僅是陣列,任何具有 Iterator 介面、且每個成員都是一個雙元素的陣列的資料結構都可以當作Map建構函式的引數。這就是說,Set和Map都可以用來生成新的 Map。

const set = new Set([
  ['foo', 1],
  ['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3

2. 屬性和操作方法

  • size屬性:返回Map的成員的數量,例如map.size,返回數字
  • Map.protottype.set(key,value):給Map設定鍵名key和鍵值value,返回Map,如果key已有值,鍵值更新,可以鏈式使用
  • Map.prototype.get(key):讀取key對應的鍵值,如果找不到就返回undefined
  • Map.prototype.has(key):返回布林值,判讀某個鍵是否在Map中
  • Map.prototype.detele(key):刪除指定鍵,成功返回true
  • Map.prototype.clear():清除所以成員,沒有返回值

3.遍歷

  • Map的遍歷方法和set基本一樣
  • Map.prototype.keys():返回鍵名的遍歷器
  • Map.prototype.values():返回鍵值的遍歷器
  • Map.prototype.entries():返回所以成員的遍歷器
  • Map.prototype.forEach():遍歷Map的所以成員
  • for…of:等同於使用map.entries()

需要特別注意的是,Map 的遍歷順序就是插入順序。

const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同於使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

4.於其他資料結構轉換

  • Map轉陣列–擴充套件運算子
const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
  • 陣列轉Map
new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])
// Map {
//   true => 7,
//   Object {foo: 3} => ['abc']
// }
  • Map轉物件–就是遍歷之後再放入物件中
    如果所有 Map 的鍵都是字串,它可以無損地轉為物件。
function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

如果有非字串的鍵名,那麼這個鍵名會被轉成字串,再作為物件的鍵名。

  • 物件轉Map
    物件轉為 Map 可以通過Object.entries()。
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));

也可以遍歷物件再set

function objToStrMap(obj) {
  let strMap = new Map();
  for (let k of Object.keys(obj)) {
    strMap.set(k, obj[k]);
  }
  return strMap;
}

objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}
  • Map轉JSON``
    Map 轉為 JSON 要區分兩種情況。一種情況是,Map 的鍵名都是字串,這時可以選擇轉為物件 JSON。就是先轉物件在用stringify轉JSON
function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'

另一種情況是,Map 的鍵名有非字串,這時可以選擇轉為陣列 JSON。就是先轉陣列在用stringify轉JSON

function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
  • JSON轉Map
    JSON 轉為 Map,正常情況下,所有鍵名都是字串。就是先轉物件,在遍歷物件set入Map
function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}

但是,有一種特殊情況,整個 JSON 就是一個陣列,且每個陣列成員本身,又是一個有兩個成員的陣列。這時,它可以一一對應地轉為 Map。這往往是 Map 轉為陣列 JSON 的逆操作。就是滿足格式直接new

function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}```

**5.注意** 

 - 如果對同一個鍵多次賦值,後面的值將覆蓋前面的值。

```javascript
const map = new Map();

map
.set(1, 'aaa')
.set(1, 'bbb');

map.get(1) // "bbb"
  • 如果讀取一個未知的鍵,則返回undefined。
new Map().get('asfddfsasadf')// undefined
  • 只有對同一個物件的引用,Map 結構才將其視為同一個鍵。這一點要非常小心。
const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined

//下面程式碼中,變數k1和k2的值是一樣的,但是它們在 Map 結構中被視為兩個鍵。
const map = new Map();

const k1 = ['a'];
const k2 = ['a'];

map
.set(k1, 111)
.set(k2, 222);

map.get(k1) // 111
map.get(k2) // 222


上面程式碼的set和get方法,表面是針對同一個鍵,但實際上這是兩個不同的陣列例項,記憶體地址是不一樣的,因此get方法無法讀取該鍵,返回undefined。
Map 的鍵實際上是跟記憶體地址繫結的,只要記憶體地址不一樣,就視為兩個鍵
如果 Map 的鍵是一個簡單型別的值(數字、字串、布林值),則只要兩個值嚴格相等,Map 將其視為一個鍵,比如0和-0就是一個鍵,布林值true和字串true則是兩個不同的鍵。另外,undefined和null也是兩個不同的鍵。雖然NaN不嚴格相等於自身,但 Map 將其視為同一個鍵。

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123

個人對阮一峰大佬的《ECMAScript入門》簡單總結,詳細請戳:
https://es6.ruanyifeng.com/?search=map&x=0&y=0#docs/set-map

相關文章