跳转到内容

JavaScript WeakMap

来自代码酷


WeakMap 是 JavaScript 中的一种特殊集合类型,用于存储键值对,其中键必须是对象(非原始值),且对键的引用是“弱引用”。这意味着当键对象不再被其他部分引用时,它会被垃圾回收机制自动清除,从而避免内存泄漏。WeakMap 是 ES6(ECMAScript 2015)引入的现代特性之一,适用于需要管理对象关联数据的场景。

基本介绍[编辑 | 编辑源代码]

WeakMap 与普通的 Map 类似,但有以下几个关键区别:

  • 键必须是对象:WeakMap 的键只能是对象(包括数组、函数等),不能是原始值(如字符串、数字等)。
  • 弱引用:WeakMap 对键的引用是弱引用,不会阻止垃圾回收机制回收键对象。
  • 不可枚举:WeakMap 没有方法可以直接遍历其键或值(如 `keys()`、`values()`、`entries()`)。
  • 无大小属性:WeakMap 没有 `size` 属性,因为其内容可能随时被垃圾回收。

WeakMap 的主要用途是存储与对象关联的私有或临时数据,而无需手动管理内存。

语法与基本用法[编辑 | 编辑源代码]

WeakMap 的构造函数和基本方法如下:

// 创建一个空的 WeakMap
const weakMap = new WeakMap();

// 键必须是对象
const key1 = { id: 1 };
const key2 = function() {};

// 设置键值对
weakMap.set(key1, "value associated with key1");
weakMap.set(key2, "value associated with key2");

// 获取值
console.log(weakMap.get(key1)); // 输出: "value associated with key1"

// 检查键是否存在
console.log(weakMap.has(key1)); // 输出: true

// 删除键值对
weakMap.delete(key1);
console.log(weakMap.has(key1)); // 输出: false

注意事项[编辑 | 编辑源代码]

  • 如果尝试使用原始值作为键(如 `weakMap.set("stringKey", "value")`),会抛出 `TypeError`。
  • 如果键对象被垃圾回收,对应的值也会从 WeakMap 中自动移除。

实际应用场景[编辑 | 编辑源代码]

WeakMap 的弱引用特性使其特别适合以下场景:

1. 私有数据存储[编辑 | 编辑源代码]

在类或模块中,WeakMap 可用于存储私有数据,避免污染对象的公共接口:

const privateData = new WeakMap();

class Person {
    constructor(name, age) {
        privateData.set(this, { name, age });
    }

    getName() {
        return privateData.get(this).name;
    }

    getAge() {
        return privateData.get(this).age;
    }
}

const person = new Person("Alice", 30);
console.log(person.getName()); // 输出: "Alice"
// 外部无法直接访问 privateData 中的数据

2. 缓存系统[编辑 | 编辑源代码]

WeakMap 可用于缓存计算结果,当缓存键不再需要时自动清理:

const cache = new WeakMap();

function computeExpensiveValue(obj) {
    if (cache.has(obj)) {
        return cache.get(obj);
    }
    const result = /* 复杂计算 */ obj.someProperty * 2;
    cache.set(obj, result);
    return result;
}

const data = { someProperty: 42 };
console.log(computeExpensiveValue(data)); // 计算并缓存
console.log(computeExpensiveValue(data)); // 从缓存读取

3. DOM 节点元数据[编辑 | 编辑源代码]

在操作 DOM 时,WeakMap 可以存储与 DOM 节点关联的元数据,当节点被移除时自动清理:

const nodeMetadata = new WeakMap();

function setNodeData(node, data) {
    nodeMetadata.set(node, data);
}

function getNodeData(node) {
    return nodeMetadata.get(node);
}

const div = document.createElement("div");
setNodeData(div, { clicked: false });
console.log(getNodeData(div)); // 输出: { clicked: false }

// 当 div 被移除时,元数据会自动清除

WeakMap 与 Map 的对比[编辑 | 编辑源代码]

特性 WeakMap Map
键类型 仅对象 任意值
键引用类型 弱引用 强引用
可遍历性 不可遍历 可遍历
内存管理 自动清理 需手动清理

graph LR A[对象作为键] --> B[WeakMap] B --> C[垃圾回收后自动移除] D[原始值作为键] --> E[Map] E --> F[需手动删除]

数学背景(可选)[编辑 | 编辑源代码]

WeakMap 的弱引用特性可以用数学关系描述: WeakMapObj×Value 其中:

  • Obj 是所有对象的集合。
  • Value 是所有可能值的集合。
  • oObj 不再可达时,(o,v) 会从 WeakMap 中移除。

总结[编辑 | 编辑源代码]

WeakMap 是 JavaScript 中一种高效的内存管理工具,特别适合以下场景:

  • 需要存储对象关联数据且不希望干扰垃圾回收。
  • 实现私有属性或模块内部状态。
  • 缓存系统或 DOM 节点元数据管理。

由于其不可枚举和弱引用的特性,WeakMap 不适用于需要频繁遍历或长期保留键的场景。正确使用 WeakMap 可以显著减少内存泄漏的风险。

模板:JavaScript Modern Features