ES6 WeakMap 核心特性与实战应用场景详解

作者:小括号

时间:2026-10-22

浏览:573

本文深入解析ES6中WeakMap的核心特性,重点阐述其键名必须为对象及弱引用机制。通过对比Map与强引用,说明WeakMap如何避免内存泄漏。文章详细列举了WeakMap在DOM数据绑定、数据缓存及模拟私有属性三大场景下的具体应用与代码示例。

文章目录

WeakMap核心特性解析

前言

文章将首先解析WeakMap的核心特性,随后探讨其具体的应用场景。

特性

第一,WeakMap的键名仅限于对象类型。


const map = new WeakMap();
map.set(1, 2);
// TypeError: Invalid value used as weak map key
map.set(null, 2);
// TypeError: Invalid value used as weak map key

第二,WeakMap对键名所引用的对象保持弱引用关系。

这一描述初看可能令人困惑,其核心含义可理解为:

WeakMaps hold "weak" references to key objects,

即WeakMap仅持有对键名对应对象的弱引用。

首先需理解弱引用的概念:

在程序设计中,弱引用不同于强引用,它不保证被引用的对象一定能存活。若对象仅被弱引用指向,则被视为不可访问(或弱可访问),随时可能被垃圾回收器回收。

在JavaScript常规操作中,创建对象通常建立的是强引用:


var obj = new Object();

唯有手动将obj设为null,被引用的对象才可能被回收。

若构建一个弱引用对象:


// 假设可以这样创建一个
var obj = new WeakObject();

无需额外操作,仅需等待垃圾回收机制运行,obj引用的对象即可被自动回收。

回到前文所述:

WeakMaps 保持了对键名所引用的对象的弱引用

以常规情况为例:


const key = new Array(5 * 1024 * 1024);
const arr = [
 [key, 1]
];

此方式实际上建立了arr对key所引用对象(假设为Obj)的强引用。

因此,当执行 key = null 操作时,仅去除了key对Obj的强引用,arr对Obj的强引用依然存在,故Obj不会被回收。

Map类型的行为与此类似:


let map = new Map();
let key = new Array(5 * 1024 * 1024);

// 建立了 map 对 key 所引用对象的强引用
map.set(key, 1);
// key = null 不会导致 key 的原引用对象被回收
key = null;

可通过Node.js环境验证该问题:


// 允许手动执行垃圾回收机制
node --expose-gc

global.gc();
// 返回 Nodejs 的内存占用情况,单位是 bytes
process.memoryUsage(); // heapUsed: 4640360 ≈ 4.4M

let map = new Map();
let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46751472 注意这里大约是 44.6M

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 46754648 ≈ 44.6M

// 这句话其实是无用的,因为 key 已经是 null 了
map.delete(key);
global.gc();
process.memoryUsage(); // heapUsed: 46755856 ≈ 44.6M

若希望Obj被回收,需先执行 delete(key),随后再执行 key = null:。


let map = new Map();
let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
map.delete(key);
key = null;

同样通过Node.js进行验证:


node --expose-gc

global.gc();
process.memoryUsage(); // heapUsed: 4638376 ≈ 4.4M

let map = new Map();
let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46727816 ≈ 44.6M

map.delete(key);
global.gc();
process.memoryUsage(); // heapUsed: 46748352 ≈ 44.6M

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 4808064 ≈ 4.6M

此时引入WeakMap:


const wm = new WeakMap();
let key = new Array(5 * 1024 * 1024);
wm.set(key, 1);
key = null;

执行wm.set(key, 1)时,wm对key引用的对象建立弱引用;但因 let key = new Array(5 * 1024 * 1024) 建立了key对该对象的强引用,对象不会被回收。然而,当执行key = null 后,仅剩wm持有的弱引用,下次垃圾回收机制运行时,该对象将被回收。

使用Node.js验证如下:


node --expose-gc

global.gc();
process.memoryUsage(); // heapUsed: 4638992 ≈ 4.4M

const wm = new WeakMap();
let key = new Array(5 * 1024 * 1024);
wm.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46776176 ≈ 44.6M

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 4800792 ≈ 4.6M

因此,WeakMap可省去手动删除对象关联数据的步骤。当无法或不愿控制关联数据生命周期时,WeakMap是理想选择。

总结弱引用特性:WeakMap保持对键名对象的弱引用,垃圾回收机制不将其计入引用计数。只要对象其他引用被清除,垃圾回收机制即释放其内存。无需手动删除,键名对象及对应键值对会在不再需要时自动消失。

正因该特性,WeakMap成员数量取决于垃圾回收机制是否运行,运行前后成员数可能不同。因垃圾回收时机不可预测,ES6规定WeakMap不可遍历。

与Map不同,WeakMap无遍历操作(无keys()、values()、entries()方法),无size属性,不支持clear方法。仅保留get()、set()、has()、delete()四个方法。

WeakMap实战应用场景

应用

一、在DOM对象上保存相关数据

传统jQuery开发中,常通过$.data()在DOM元素上存储信息(如删除按钮存储帖子ID)。jQuery内部使用对象管理DOM与数据关联,删除DOM元素后,关联数据不会自动清除,需手动调用$.removeData()。使用WeakMap可简化此操作:


let wm = new WeakMap(), element = document.querySelector(".element");
wm.set(element, "data");

let value = wm.get(elemet);
console.log(value); // data

element.parentNode.removeChild(element);
element = null;

二、数据缓存

前述示例表明,当需关联对象与数据(如不修改原对象存储属性或计算值),且无需管理数据生命周期时,WeakMap非常适合。数据缓存即为典型用例:


const cache = new WeakMap();
function countOwnKeys(obj) {
  if (cache.has(obj)) {
    console.log('Cached');
    return cache.get(obj);
  } else {
    console.log('Computed');
    const count = Object.keys(obj).length;
    cache.set(obj, count);
    return count;
  }
}

三、私有属性

WeakMap可用于实现私有变量,ES6中实现私有变量有多种方式,此为其中之一:


const privateData = new WeakMap();

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

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

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

export default Person;

以上为本文全部内容,希望对学习有所帮助,欢迎支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章