JS笔记《Map与WeakMap数据结构》

150 阅读4分钟

Map概述

  • JS中的对象,本质上是键值对的集合,只能用字符串或Symbol当作键名。Map数据结构类似对象,也是键值对的集合,区别是键名不限于字符串,可以使用各种类型的值做当键名。
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"
  • Map也可以接受一个数组(或者具有Iterator接口的其他数据结构)作为参数,该数组的成员是一个个键值对的数组。
const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
  • 只有对同一个对象的引用,Map才将其视为同一个键。
const map = new Map();
map.set(['a'], 1);
map.get(['a'])  // undefiend 与set进去的数组不是同一个引用
  • 如果键名是一个原始值,则只要两个值严格相等就会被视为同一个键。
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  虽然在严格相等中NaN不等于NaN,但在Map中是一样的

实例属性

size

  • 返回成员总数。
const map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2

实例操作方法

set()

  • 设置键名以及对应的键值,然后返回整个Map。如果对应的key已经有值,则该键值会被更新。
  • 因为set()会返回Map,因此可以采用链式写法。
let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');

get()

  • 读取key对应的键值,如果找不到返回undefined
const m = new Map();

const hello = function() {
   console.log('hello');
};

m.set(hello, 'Hello ES6!') // 键是函数
m.get(hello)  // Hello ES6!

has()

  • 返回一个布尔值,表示某个键是否在当前Map对象中。
const m = new Map();

m.set('edition', 6);

m.has('edition')     // true
m.has('years')       // false

delete()

  • 删除某个key,返回布尔值,如果删除失败返回false
const m = new Map();
m.set(undefined, 'nah');

m.delete(undefined)  // true

clear()

  • 清除所有成员,没有返回值。
let map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2
map.clear()
map.size // 0

实例遍历方法

  • Map的遍历顺序就是插入顺序。keys()values()entries()返回的都是遍历器对象(Iterator)。

keys()

  • 返回键名的遍历器。

values()

  • 返回键值的遍历器。

entries()

  • 返回键值对的遍历器。
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"

forEach()

  • 使用回调函数遍历每个成员。需要注意的是参数一是键值;参数二是键名
const map = new Map()
  .set('a', 1)
  .set('b', 2)
  .set('c', 3);

map.forEach((value, key) => {
  console.log(key, value)
})
// a 1
// b 2
// c 3

与其他数据结构的转换

Map 转为数组

  • 使用扩展运算符。
const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);
  
[...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

数组转为 Map

  • 将数组作为参数传入Map构造函数。
new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])

// Map {
//   true => 7,
//   Object {foo: 3} => ['abc']
// }

Map 转为对象

  • 如果Map的键都是字符串,那么可以无损转为对象。如果有非字符串的键名,会先转为字符串。
let c = [1, 2, 3];
const map = new Map()
  .set('a', 1)
  .set('b', 2)
  .set(c, 3);

function map2Obj(map) {
  let obj = {};
  for (const [key, value] of map) {
    obj[key] = value;
  }
  return obj;
}

let o = map2Obj(map);
o  // {a: 1, b: 2, 1,2,3: 3}  // 数组 c 被转为了字符串 1,2,3

对象转为 Map

  • 通过Object.entries()
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));

WeakMap概述

  • Map结构类似,也是键值对的集合。与Map的区别一是WeakMap的键名只能是对象Symbol;区别二是WeakMap的键名所引用的对象都是弱引用,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。
  • 没有遍历操作(即没有keys()values()entries()方法),也没有size属性。
new WeakMap()
  .set({a: 1}, 1)
  .set({b: 2}, 2);

实例操作方法

get()

  • 读取key对应的键值,如果找不到返回undefined

set()

  • 设置键名以及对应的键值,然后返回整个WeakMap。如果对应的key已经有值,则该键值会被更新。
  • 因为set()会返回WeakMap,因此可以采用链式写法。

has()

  • 返回一个布尔值,表示某个键是否在当前Map对象中。

delete()

  • 删除某个key,返回布尔值,如果删除失败返回false