JavaScript的Map和WeakMap
阅读原文时间:2023年08月11日阅读:1

熟悉JavaScript的Map和WeakMap

Map的键/值可以是任何类型

基本API

初始化映射

//使用new关键字和Map构造函数进行初始化
const m1 = new Map();

//使用嵌套数组初始化映射
const m2 = new Map([
  ['k1', 'v1'],
  ['k2', 'v2'],
  ['k3', 'v3']
]);

// 使用自定义迭代器初始化
const m3 = new Map({
  [Symbol.iterator]:function*() {
    yield ['k1', 'v1'];
    yield ["k2", "v1"];
    yield ["k3", "v3"];
  }
})

Map的方法:set()get()has()delete()clear()

Map的属性:size

m2.set('k4', 'v4');
console.log(m2.has('k4'));//true
console.log(m2.get('k4'));//v4
m2.delete('k4');
console.log(m2.has("k4")); //false
console.log(m2.get("k4"));//undefined
m2.clear();
console.log(m2.has('k1'));//false

set()返回映射实例,于是我们可以链式调用set():

m2.set('k4','v4')
    .set('k5','v5');

Map的键的相等比较

键的比较基于零值相等(SameValueZero),它认为+0与-0、NaN与NaN是相等的,其他的所有键通过===来比较相等性。

m2.set(NaN, 1);
m2.set(NaN,2);
console.log(m2.get(NaN));//2

m2.set(-0, 3);
console.log(m2.get(+0));//3

Map和Object的比较

Object也可以用来完成映射的作用,那选Map还是Object呢?

从以下方面决策:

  1. 内存占用:Object和Map在不同浏览器上的实现有差异,但是都会随着键值对增加占用内存线性增长,同时在给定相同的内存情况下,Map能比Object多存储50%的键值对。
  2. 插入性能:Map的插入比Object稍微好一点儿。
  3. 查找速度:如果代码涉及大量查找操作,那么选择Object会更好。
  4. 删除性能:如果代码涉及大量删除操作,那么选择Map会更好。
  5. 迭代:Map实现了迭代协议,Object没有。
  6. size:Map的键值对数量可以通过size属性轻松获得,而Object只能手动计算。
  7. 序列化和解析:Map没有序列化和解析支持。

可以实现Map的序列化和解析:How do you JSON.stringify an ES6 Map?

function replacer(key, value) {
if (value instanceof Map) {
 return {
   dataType: "Map",
   value: Array.from(value.entries()), // or with spread: value: [...value]
 };
}
return value;
}
function reviver(key, value) {
if (typeof value === "object" && value !== null) {
 if (value.dataType === "Map") {
   return new Map(value.value);
 }
}
return value;
}
const originalValue = new Map([["a", 1]]);
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);

WeakMap称为弱映射。"弱"指的是内存回收机制对待WeakMap中键的方式。

基本API

弱映射的键只能是对象类型(Object或继承自Object)。值类型无限制。

方法和属性的名称和Map相同,除了没有clear()。

弱键

WeakMap中的键不是正式的引用,不会阻止垃圾回收。只要键存在,键值对就会存在于映射中。

const wm = new WeakMap();
wm.set({}, 'v1');

wm新建的键值对的键对象没有被引用,所以垃圾回收机制会回收它。

const wm = new WeakMap();
let obj = new Object();
wm.set(obj, 'v1');
console.log(wm.has(obj));//true
obj = null;
console.log(wm.has(obj));//false

不可迭代键

WeakMap没有提供迭代键值对的功能,也没有必要,是为了保证只有通过键对象的引用才能取得值。

弱映射的实际使用

私有变量

以对象实例为键,以私有成员的字典为值。

const wm = new WeakMap();
class User{
  constructor(id) {
    this.userId = Symbol('id');
    this.setId(id);
  }
  setId(id) {
    const privateMembers = wm.get(this) || {};
    privateMembers[this.userId] = id;
    wm.set(this, privateMembers);
  }
  getId() {
    return wm.get(this)[this.userId];
  }
}
const user = new User(1);
console.log(user.getId());//1
user.setId(2);
console.log(user.getId());//2

上面的变量userId并非真正私有,通过弱映射vm可以获取:

console.log(wm.get(user)[user.userId]);//2

我们可以把弱映射和类放在一个闭包里:

const User = (() => {
  const wm = new WeakMap();
  class User {
    constructor(id) {
      this.userId = Symbol("id");
      this.setId(id);
    }
    setId(id) {
      const privateMembers = wm.get(this) || {};
      privateMembers[this.userId] = id;
      wm.set(this, privateMembers);
    }
    getId() {
      return wm.get(this)[this.userId];
    }
  }
  return User;
})();
const user = new User(1);
console.log(user.getId());//1
user.setId(2);
console.log(user.getId());//2

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器