JavaScript中WeakMap与Map

99 阅读4分钟

WeakMap与Map的区别

WeakMap

WeakMap 是 JavaScript 中的一种集合数据结构,专门设计用于存储键值对,其中键必须是对象,而值可以是任意类型。WeakMap 的设计有几个重要的目的和特点:

1. 弱引用键

WeakMap 的键是弱引用的,这意味着如果一个对象只作为 WeakMap 的键存在,而没有其他引用指向它,那么这个对象可以被垃圾回收机制回收。这有助于防止内存泄漏。

2. 私有数据存储

WeakMap 通常用于在对象上存储私有数据。由于 WeakMap 的键是弱引用的,且外部无法直接访问 WeakMap 内部的数据,因此可以安全地存储一些敏感信息或内部状态,而不必担心外部代码的干扰。

3. 生命周期管理

WeakMap 的键对象的生命周期由垃圾回收机制管理,而不是由 WeakMap 本身管理。当一个对象不再被其他任何地方引用时,即使它仍然存在于 WeakMap 中,也会被自动删除。这使得 WeakMap 非常适合用于缓存或其他需要根据对象生命周期动态管理数据的场景。

4. 不可枚举

WeakMap 不提供任何方法来枚举或获取所有键,这进一步增强了其私有性和安全性。你不能遍历 WeakMap 的所有条目,也不能获取其大小,这使得 WeakMap 适合用于存储不需要公开访问的数据。 下面是一个简单的 WeakMap 使用示例,展示了如何在对象上存储私有数据:

  const privateData = new WeakMap();

  class MyClass {
    constructor(data) {
      privateData.set(this, data);
    }

    getData() {
      return privateData.get(this);
    }

    setData(data) {
      privateData.set(this, data);
    }
  }

  const instance = new MyClass({ key: 'value' });

  console.log(instance.getData()); // 输出: { key: 'value' }
  instance.setData({ key: 'new value' });
  console.log(instance.getData()); // 输出: { key: 'new value' }

在这个例子中,privateData 是一个 WeakMap,用于在 MyClass 实例上存储私有数据。外部代码无法直接访问 privateData,只能通过 MyClass 的方法来操作这些数据。

Map

Map 本质上就是键值对的集合,但是普通的Object中的键值对中的键只能是字符串。而ES6提供的Map数据结构类似于对象,但是它的键不限制范围,可以是任意类型,是一种更加完善的Hash结构。如果Map的键是一个原始数据类型,只要两个键严格相同,就视为是同一个键。 Map数据结构有以下操作方法:

  • size: map.size 返回Map结构的成员总数。
  • set(key,value) :设置键名key对应的键值value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就新生成该键。(因为返回的是当前Map对象,所以可以链式调用)
  • get(key) :该方法读取key对应的键值,如果找不到key,返回undefined。
  • has(key) :该方法返回一个布尔值,表示某个键是否在当前Map对象中。
  • delete(key) :该方法删除某个键,返回true,如果删除失败,返回false。
  • clear() :map.clear()清除所有成员,没有返回值。

Map结构原生提供是三个遍历器生成函数和一个遍历方法

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历Map的所有成员。

WeakMap 和 Map,其区别如下:

  1. 键的类型
    Map: 键可以是任何数据类型,包括原始值(如 number、string、boolean)和对象。
    WeakMap: 键必须是对象。不能使用原始值作为键。
  2. 弱引用
    Map: 键和值都是强引用,这意味着只要键或值被 Map 引用,它们就不会被垃圾回收机制回收。 WeakMap: 键是弱引用的,这意味着如果一个对象只作为 WeakMap 的键存在,而没有其他引用指向它,那么这个对象可以被垃圾回收机制回收。值仍然是强引用的。
  3. 可枚举性
    Map: 提供了多种方法来遍历和操作集合,如 entries()、keys()、values()、forEach() 等。 可以获取 Map 的大小(即键值对的数量)。 WeakMap: 不提供任何方法来遍历或获取所有键,这进一步增强了其私有性和安全性。 不能获取 WeakMap 的大小。
  4. 初始值
    Map: 可以通过一个可迭代对象(如数组)初始化。
    例如:const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
    WeakMap: 只能通过构造函数初始化,不能通过可迭代对象初始化。
    例如:const weakMap = new WeakMap([[obj1, 'value1'], [obj2, 'value2']]);
  5. 应用场景
    Map: 适合需要存储任意类型键值对的场景。 适合需要遍历或操作集合的场景。 适合需要明确控制键值对生命周期的场景。 WeakMap: 适合需要存储私有数据的场景,特别是当这些数据应该随着对象的销毁而自动清除时。 适合需要避免内存泄漏的场景,特别是当键对象的生命周期不确定时。 适合需要根据对象生命周期动态管理数据的场景。