JavaScript WeakMap的具体使用

作者:闭包旅人

时间:2026-10-26

浏览:632

本文深入解析JavaScript中WeakMap的核心机制及其与Map的区别。重点阐述WeakMap如何通过弱引用键避免内存泄漏,适用于处理复杂对象深拷贝中的循环引用问题。文章对比了两者在时间复杂度、枚举能力及内存占用上的差异,并详细说明了WeakMap的四个原型方法及其在特定场景下的最佳实践。

文章目录

前言

在开发复杂对象的深拷贝逻辑时,我遇到了循环引用导致的内存问题,由此引入了WeakMap作为缓存结构来解决这一挑战。为了优化现有方案,我对WeakMap进行了深入研究。

一、为何选用WeakMap

虽然Map和WeakMap都支持以对象作为键,且Map额外支持基本数据类型作为键,但在仅使用对象作为键的特殊场景下,两者的表面行为看似一致。

1. Map

Map会阻碍JavaScript垃圾回收机制回收那些以已回收对象为键的元素。这与Map内部存储元素的方式密切相关:

A map API could be implemented in JavaScript with two arrays (one for keys, one for values) shared by the four API methods. `map`在JavaScript内可通过使两个数组被4种方法共享来实现, 向`Map`内设置元素时分别将键和值`push`进这两个数组. Setting elements on this map would involve pushing a key and value onto the end of each of those arrays simultaneously. 访问`Map`取值时需要遍历, 从两个数组内找到匹配的值.

var sayings = new Map();
sayings.set('dog', 'woof');
sayings.set('cat', 'meow');
sayings.set('elephant', 'toot');
sayings.size; // 3
sayings.get('fox'); // undefined
sayings.has('bird'); // false
sayings.delete('dog');
sayings.has('dog'); // false

for (var [key, value] of sayings) {
  console.log(key + ' goes ' + value);
}
// "cat goes meow"
// "elephant goes toot"

sayings.clear();
sayings.size; // 0

首先,Map内部基于数组实现,其取值和赋值操作需要遍历数组,导致时间复杂度增加:

The first one is an O(n) set and search (n being the number of keys in the map) 首先设置和查找的时间复杂度都是O(n), n是Map内键的数量. since both operations must iterate through the list of keys to find a matching value. 因为两个操作都必须遍历每一个键以找到匹配的值.

其次,数组会一直保留对元素的引用,无论元素是否为引用类型。只要数组本身不被销毁,其中存储的元素引用也不会被销毁:

let ele1 = 'ele1';
let ele2 = { key: 'value' };

let arr = [ ele1, ele2 ];

ele1 = null; // 设置为null脱离当前执行环境, 以便回收机制回收
ele2.key = null;

console.log(arr);
// [ 'ele1', { key: 'null' } ]

前文提到Map内部可通过两个数组实现,这意味着在Map对象销毁之前,其内部的键和值都无法被JavaScript垃圾回收机制回收。这可能导致Map变得臃肿庞大,进而引发内存不足甚至内存泄漏。

持有原始对象引用的映射实际上意味着对象不能被垃圾回收, 这可能会导致意外的内存问题. 如果你希望存储在映射中的对象具有与原始对象相同的生命周期, 请考虑使用 WeakMap.

2. WeakMap

WeakMap同样是键值对的集合,但其键被弱持有。当键所指的对象不再被其他地方引用时,垃圾回收机制会将其回收。WeakMap的键必须是对象,而值可以是任意数据类型。

由于这种弱引用机制允许垃圾回收执行,WeakMap内元素的存在变得不可预知(可能垃圾回收机制尚未回收至此处,但理论上应当回收)。为了防止执行时出现意外,WeakMap不提供枚举方法。

正由于这样的弱引用, WeakMap的key是不可枚举的(没有方法能给出所有的 key). 如果key可枚举, 其列表将受垃圾回收机制的影响, 从而得到不确定的结果。

尽管WeakMap不提供枚举功能,但它提供了与Map相同的接口,允许通过稳定的接口访问元素。

let ele1 = { key: "value" };

let weak = new WeakMap();
weak.set(ele1, "ele1");

ele1 = null;

console.log(weak); // WeakMap { <items unknown> } 不赋值null此处结果相同
console.log(weak.get(ele1)); // undefined 销毁 不赋值null此处为'ele1'

总之,相比Map,WeakMap更简洁且更节约内存。但如果存在枚举需求,或者需要确保key不被回收,则应选择Map。

二、WeakMap原型方法

明确所需操作,并指定目标键值对的key,直接传入变量即可执行。

方法描述
WeakMap.prototype.delete(key)删除WeakMap中与key相匹配的value.
WeakMap.prototype.get(key)返回WeakMap中与key相关联的值, 如果key不存在则返回undefined.
WeakMap.prototype.has(key)返回布尔值, 断言WeakMap对象中该key是否存在.
WeakMap.prototype.set(key, value)向WeakMap中设置一组新的键值对, 返回设置完毕后的WeakMap对象.

总结

到此这篇关于JavaScript WeakMap的具体使用的文章就介绍到这了,更多相关JavaScript WeakMap内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章