WeakMap 和 WeakSet:解决内存泄漏避免循环引用(上)

2023-12-22 06:28

本文主要是介绍WeakMap 和 WeakSet:解决内存泄漏避免循环引用(上),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6
🍨 阿珊和她的猫_CSDN个人主页
🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》
🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》

文章目录

  • 一、引言
    • 介绍WeakMap 和 WeakSet 的背景和用途
  • 二、 WeakMap 简介
    • 定义和语法
    • 与普通 Map 的区别
    • WeakMap 的键必须是对象,值可以是任意类型
    • 如何创建和使用 WeakMap
  • 三、 WeakSet 简介
    • 定义和语法
    • 与普通 Set 的区别
    • WeakSet 只包含对象
    • 如何创建和使用 WeakSet

一、引言

介绍WeakMap 和 WeakSet 的背景和用途

以下是WeakMap和WeakSet的背景和用途介绍:

WeakMap类似于Map,但是键只能是对象类型且键名所指向的对象是弱引用,这意味着如果这个对象在其他地方没有被引用,那么它将会被垃圾回收,这也是WeakMap的主要应用场景。

WeakSet类似于Set,但成员只能是对象类型,且成员对象是弱引用,这意味着如果这个对象在其他地方没有被引用,那么它将会被垃圾回收,这也是WeakSet的主要应用场景。

总的来说,WeakMap和WeakSet主要用于解决内存泄漏问题,避免循环引用,以及在一些临时数据存储和缓存的场景中提高性能。

二、 WeakMap 简介

定义和语法

WeakMap是ES6中新增的一种数据结构,用于存储键值对。它与Map类似,但有一些重要区别:

  • 键必须是对象类型,值可以是任意类型。
  • 由于键是弱引用,如果对键的所有引用都丢失,并且不再有对值的引用,则该值可能会被垃圾回收。

以下是WeakMap的定义和语法示例:

new WeakMap()  // 创建一个新的WeakMap对象
new WeakMap(iterable)  // iterable是数组或者任意可以迭代的对象,需要拥有key-value对(一般是一个二维数组)

WeakMap 实例方法包括:

  • delete(key):删除与键关联的任意值。删除后,has(key)返回false
  • get(key):返回与键关联的值,如果不存在关联值,则返回undefined
  • has(key):返回键在 WeakMap 上是否存在的结果。
  • set(key, value):在 WeakMap 对象上为对应键设置指定的值,并返回 WeakMap 对象。

与普通 Map 的区别

WeakMap 与普通 Map 的主要区别在于它们对键的处理方式不同。

普通 Map 的键必须是可枚举的(即能够通过for...of循环遍历),并且是强引用的。这意味着只要有对 Map 中键的引用存在,这个键就不会被垃圾回收。

而 WeakMap 的键必须是对象类型,并且是弱引用的。这意味着如果除了WeakMap 本身之外,没有其他对键的引用存在,那么这个键和对应的值都可能会被垃圾回收。

以下是一个示例,展示了 WeakMap 与普通 Map 在键的生命周期上的区别:

// 创建一个普通 Map
const map = new Map();
const obj1 = {key: 'value1'};
map.set(obj1, 'data');// 创建一个 WeakMap
const weakMap = new WeakMap();
const obj2 = {key: 'value2'};
weakMap.set(obj2, 'data');// 断开对 obj1 和 obj2 的引用
obj1 = null;
obj2 = null;// 检查普通 Map 中键是否存在
console.log(map.has(obj1)); 
console.log(map.get(obj1)); // 检查 WeakMap 中键是否存在
console.log(weakMap.has(obj2)); 
console.log(weakMap.get(obj2)); 

在这个示例中,我们创建了一个普通 Map 和一个 WeakMap,并将两个对象作为键分别存储在它们里面。然后,我们断开了对这两个对象的引用。

对于普通 Map,由于键是强引用的,即使我们断开了对对象的引用,键仍然存在于 Map 中,并且可以通过map.has(obj1)map.get(obj1)来检查和获取对应的值。

对于 WeakMap,由于键是弱引用的,当我们断开了对对象的引用后,WeakMap 无法检测到键的存在,因此weakMap.has(obj2)返回false,并且weakMap.get(obj2)返回undefined

这就是 WeakMap 和普通 Map 在键的处理方式上的主要区别。WeakMap 适用于在某些情况下需要避免内存泄漏的场景,例如缓存、临时数据存储等。

WeakMap 的键必须是对象,值可以是任意类型

以下是一个使用 WeakMap 的简单代码示例:

// 创建一个 WeakMap 对象
const weakMap = new WeakMap();// 创建两个对象作为 WeakMap 的键
const obj1 = { key: 1 };
const obj2 = { key: 2 };// 将值与对应的对象键关联起来
weakMap.set(obj1, 'value1');
weakMap.set(obj2, 'value2');// 检查键是否存在
console.log(weakMap.has(obj1));  
console.log(weakMap.has(obj2)); // 获取对应键的值
console.log(weakMap.get(obj1));  
console.log(weakMap.get(obj2));  // 删除键值对
weakMap.delete(obj1);// 检查键是否存在(现在应该为 false)
console.log(weakMap.has(obj1));  

在这个示例中,我们创建了一个 WeakMap 对象,并将两个对象作为键与一些值关联起来。然后,我们检查键是否存在,并获取对应的值。最后,我们删除了一个键值对,并再次检查该键是否存在。

请注意,WeakMap 的键必须是对象,而值可以是任意类型。另外,WeakMap 不会阻止垃圾回收对键的回收,因此如果没有其他对键的引用,对应的键值对可能会被自动删除。

如何创建和使用 WeakMap

WeakMap 是一种用于从外部扩展对象而不干扰垃圾回收的 JavaScript 数据结构。它是一个 Map 字典,其中的键很弱,即如果对该键的所有引用都丢失,并且不再有对该值的引用,则可以对该值进行垃圾回收。

与普通 Map 相比,WeakMap 的键必须是对象,而值可以是任意类型。

创建和使用 WeakMap 的方法如下:

let weakMap = new WeakMap();
let o = {n: 1};
weakMap.set(o, "A"); // 添加
console.log(weakMap.has(o)); // 检查键是否存在
console.log(weakMap.get(o)); // 检索与键关联的值

三、 WeakSet 简介

定义和语法

WeakSet 是ES6中引入的一种新的数据结构,它类似于数组,但是成员的值都是唯一的,没有重复的值。与Set不同的是,WeakSet 的成员都是对象且都是弱引用,这意味着它们可以被垃圾回收机制回收,因此可以用来保存 DOM 节点,不容易造成内存泄漏

WeakSet 的语法与Set类似,也是一个构造函数,可以使用new命令创建WeakSet数据结构。作为构造函数,WeakSet可以接受一个数组或类似数组的对象作为参数。

WeakSet 结构有以下三个方法:

  • WeakSet.prototype.add(value): 向WeakSet实例添加一个新成员。
  • weakSet.prototype.delete(value): 清除WeakSet实例的指定成员。
  • weakSet.prototype.has(value): 返回一个布尔值,表示某个值是否在WeakSet实例中。

需要注意的是,WeakSet 不能遍历,因为成员都是弱引用,随时可能会消失,遍历机制无法保证成员存在。

与普通 Set 的区别

WeakSet 与普通 Set 的主要区别在于它们对成员的存储方式不同。

普通 Set 的成员是强引用的,这意味着只要 Set 中存在对某个对象的引用,这个对象就不会被垃圾回收器回收。

而 WeakSet 的成员是弱引用的,这意味着如果除了 WeakSet 之外没有其他对某个对象的引用,那么这个对象可能会被垃圾回收器回收。

以下是一个示例,展示了 WeakSet 与普通 Set 在成员的生命周期上的区别:

// 创建一个普通 Set
const strongSet = new Set();
const obj1 = {key: 'value1'};
strongSet.add(obj1);// 创建一个 WeakSet
const weakSet = new WeakSet();
const obj2 = {key: 'value2'};
weakSet.add(obj2);// 断开对 obj1 和 obj2 的引用
obj1 = null;
obj2 = null;// 检查普通 Set 中键是否存在
console.log(strongSet.has(obj1)); 
console.log(strongSet.has(obj2)); // 检查 WeakSet 中键是否存在
console.log(weakSet.has(obj1)); 
console.log(weakSet.has(obj2)); 

在这个示例中,我们创建了一个普通 Set 和一个 WeakSet,并将两个对象作为成员添加到它们里面。然后,我们断开了对这两个对象的引用。

对于普通 Set,由于成员是强引用的,即使我们断开了对对象的引用,Set 仍然能够检测到成员的存在,因此strongSet.has(obj1)strongSet.has(obj2)仍然返回true

对于 WeakSet,由于成员是弱引用的,当我们断开了对对象的引用后,WeakSet 无法检测到成员的存在,因此weakSet.has(obj1)weakSet.has(obj2)返回false

这就是 WeakSet 和普通 Set 在成员的生命周期上的主要区别。WeakSet 适用于在某些情况下需要避免内存泄漏的场景,例如缓存、临时数据存储等。

WeakSet 只包含对象

WeakSet 只包含对象,而不能包含其他类型的值,例如原始类型(如数字、字符串等)或函数等。如果试图向 WeakSet 添加非对象类型的值,将会抛出一个 TypeError 错误。

这是因为 WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,而不考虑该对象还存在于 WeakSet 之中。

如何创建和使用 WeakSet

要创建一个WeakSet,可以使用new关键字和WeakSet构造函数,示例如下:

const weakSet = new WeakSet();

由于WeakSet的元素必须是对象,而不能是原始类型(如字符串、数字等),并且WeakSet不可迭代,因此不能使用for...of循环遍历其中的元素,也没有size属性和forEach方法。

WeakSet的主要方法有add()delete()has(),用于向WeakSet实例添加新成员、清除指定成员以及判断某个值是否在WeakSet实例中。

由于WeakSet的成员都是弱引用,随时可能消失,因此遍历机制无法保证成员存在。WeakSet适合用于临时存放一组对象以及存放跟对象绑定的信息,只要这些对象在外部消失,它在WeakSet里面引用的就会自动消失,可以更好地管理对象的生命周期并避免内存泄漏的问题。

这篇关于WeakMap 和 WeakSet:解决内存泄漏避免循环引用(上)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/522879

相关文章

Linux虚拟机不显示IP地址的解决方法(亲测有效)

《Linux虚拟机不显示IP地址的解决方法(亲测有效)》本文主要介绍了通过VMware新装的Linux系统没有IP地址的解决方法,主要步骤包括:关闭虚拟机、打开VM虚拟网络编辑器、还原VMnet8或修... 目录前言步骤0.问题情况1.关闭虚拟机2.China编程打开VM虚拟网络编辑器3.1 方法一:点击还原VM

Flask解决指定端口无法生效问题

《Flask解决指定端口无法生效问题》文章讲述了在使用PyCharm开发Flask应用时,启动地址与手动指定的IP端口不一致的问题,通过修改PyCharm的运行配置,将Flask项目的运行模式从Fla... 目录android问题重现解决方案问题重现手动指定的IP端口是app.run(host='0.0.

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

mysql关联查询速度慢的问题及解决

《mysql关联查询速度慢的问题及解决》:本文主要介绍mysql关联查询速度慢的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql关联查询速度慢1. 记录原因1.1 在一次线上的服务中1.2 最终发现2. 解决方案3. 具体操作总结mysql

Python中Windows和macOS文件路径格式不一致的解决方法

《Python中Windows和macOS文件路径格式不一致的解决方法》在Python中,Windows和macOS的文件路径字符串格式不一致主要体现在路径分隔符上,这种差异可能导致跨平台代码在处理文... 目录方法 1:使用 os.path 模块方法 2:使用 pathlib 模块(推荐)方法 3:统一使

一文教你解决Python不支持中文路径的问题

《一文教你解决Python不支持中文路径的问题》Python是一种广泛使用的高级编程语言,然而在处理包含中文字符的文件路径时,Python有时会表现出一些不友好的行为,下面小编就来为大家介绍一下具体的... 目录问题背景解决方案1. 设置正确的文件编码2. 使用pathlib模块3. 转换路径为Unicod

Spring MVC跨域问题及解决

《SpringMVC跨域问题及解决》:本文主要介绍SpringMVC跨域问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录跨域问题不同的域同源策略解决方法1.CORS2.jsONP3.局部解决方案4.全局解决方法总结跨域问题不同的域协议、域名、端口

SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法

《SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法》本文主要介绍了SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法,具有一定的参考价值,感兴趣的可以了解一下... 目录方法1:更改IDE配置方法2:在Eclipse中清理项目方法3:使用Maven命令行在开发Sprin

SpringBoot自定义注解如何解决公共字段填充问题

《SpringBoot自定义注解如何解决公共字段填充问题》本文介绍了在系统开发中,如何使用AOP切面编程实现公共字段自动填充的功能,从而简化代码,通过自定义注解和切面类,可以统一处理创建时间和修改时间... 目录1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR