ES6 WeakMap 核心特性与实战应用场景详解
本文深入解析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;
以上为本文全部内容,希望对学习有所帮助,欢迎支持本站。
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
