我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JavaScript 弱引用强引用底层示例详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JavaScript 弱引用强引用底层示例详解

正文

内存和性能管理是软件开发的重要方面,也是每个软件开发人员都应该注意的方面。虽然弱引用很有用,但在 JavaScript 中并不经常使用。在 ES6 版本中,JavaScript 引入了 WeakSetWeakMap

1. 弱引用

与强引用不同,弱引用并不阻止被引用的对象被垃圾收集器回收或收集,即使它是内存中对对象的唯一引用。

在讨论强引用、WeakSetSetWeakMapMap 之前,让我们用下面的代码片段来演示弱引用:

// 创建 WeakMap 对象的实例
let human = new WeakMap();
// 创建一个对象,并将其赋值给名为 man 的变量
let man = { name: "xiaan" };
// 调用 human 的 set 方法,并传递两个参数(键和值)给它
human.set(man, "done")
console.log(human)

以上代码的输出如下:

WeakMap {{…} => 'done'}
man = null;
console.log(human)

当我们将 man 变量重新赋值为 null 时,内存中对原始对象的唯一引用是弱引用,它来自我们前面创建的 WeakMap。当 JavaScript 引擎运行垃圾收集过程时,man 对象将从内存和我们分配给它的 WeakMap 中删除。这是因为它是一个弱引用,并且它不阻止垃圾收集。接下来我们谈谈强引用。

2. 强引用

JavaScript 中的强引用是防止对象被垃圾回收的引用。它将对象保存在内存中。

下面的代码片段说明了强引用的概念:

let man = {name: "xiaan"};
let human = [man];
man =  null;
console.log(human);

以上代码的结果如下:

// 长度为 1 的对象数组
[{…}]

由于 human 数组和对象之间存在强引用。对象被保留在内存中,可以通过以下代码访问:

console.log(human[0])

这里要注意的重要一点是,弱引用不会阻止对象被垃圾回收,而强引用却会阻止对象被垃圾回收。

3. JavaScript 的垃圾收集

与所有编程语言一样,在编写 JavaScript 时,内存管理是需要考虑的关键因素。与 C 语言不同,JavaScript 是一种高级编程语言,它在创建对象时自动分配内存,在不再需要对象时自动清除内存。当不再使用对象时清除内存的过程称为垃圾收集。在谈论 JavaScript 中的垃圾收集时,几乎不可能不触及可达性的概念。

3.1 可达性

在特定作用域中的所有值或在作用域中使用的所有值都被称为在该作用域中的“可达”,并被称为“可达值”。可访问的值总是存储在内存中。

在以下情况下,值被认为是可达的:

  • 程序根中的值或从根中引用的值,如全局变量或当前执行的函数、它的上下文和回调。
  • 通过引用或引用链从根中访问的值(例如,全局变量中的对象引用另一个对象,该对象也引用另一个对象——这些都被认为是可访问的值)。

下面的代码片段说明了可达性的概念:

var person = {name: "xiaan"};

这里我们有一个对象,它的键值对(name"xiaan")引用全局变量 person。如果我们通过赋值 null 来覆盖 person 的值:

person = null;

那么对象将被垃圾回收,"xiaan" 值将无法再次访问。下面是另一个例子:

var person = {name: "xiaan"};
var programmer = person;

从上面的代码片段中,我们可以从 person 变量和 programmer 变量访问 object 属性。然而,如果我们将person 设置为 null

person = null;

那么对象仍然在内存中,因为它可以通过 programmer 变量访问。简单地说,这就是垃圾收集的工作方式。

注意:默认情况下,JavaScript 对其引用使用强引用。要在 JavaScript 中实现弱引用,可以使用 WeakMapWeakSetWeakRef

4. Set VS WeakSet

set 对象是一个只有一次出现的唯一值的集合。像数组一样,集合没有键值对。我们可以使用for...of.forEach 的数组方法遍历。

让我们用以下片段来说明这一点:

let setArray = new Set(["Joseph", "Frank", "John", "Davies"]);
for (let names of setArray){
  console.log(names)
}// Joseph Frank John Davies

我们也可以使用 .forEach 遍历:

 setArray.forEach((name, nameAgain, setArray) =>{
   console.log(names);
 });

WeakSet 是唯一对象的集合。正如其名称一样,弱集使用弱引用。以下是 WeakSet() 的特性:

  • 它可能只包含对象。
  • 集合中的对象可以在其他地方访问。
  • 它不能循环遍历。
  • Set() 一样,WeakSet()addhasdelete 方法。

下面的代码说明了如何使用 WeakSet() 和一些可用的方法:

const human = new WeakSet();
let person = {name: "xiaan"};
human.add(person);
console.log(human.has(person)); // true
person = null;
console.log(human.has(person)); // false

在第 1 行,我们创建了 WeakSet() 的一个实例。在第 3 行,我们创建了对象并将它分配给变量 person。在第 5 行,我们将 person 添加到 WeakSet() 中。在第 9 行,我们将 person 引用设为空。第 11 行代码返回false,因为 WeakSet() 将被自动清除,因此,WeakSet() 不会阻止垃圾回收。

5. Map VS WeakMap

我们从上面关于垃圾收集的部分了解到,只要可以访问,JavaScript 引擎就会在内存中保留一个值。让我们用一些片段来说明这一点:

let person = {name: "xiaan"};
// 对象可以从引用中访问
// 覆盖引用 person.
person = null;
// 该对象不能被访问

当数据结构在内存中时,数据结构的属性被认为是可访问的,并且它们通常保存在内存中。如果将对象存储在数组中,那么只要数组在内存中,即使没有其他引用,也仍然可以访问对象。

let person = {name: "xiaan"};
let arr = [person];
// 覆盖引用
person = null;
console.log(array[0]) // {name: 'xiaan'}

即使引用被覆盖,我们仍然能够访问这个对象因为对象被保存在数组中。因此,只要数组仍然在内存中,它就保存在内存中。因此,它没有被垃圾回收。由于我们在上面的例子中使用了数组,我们也可以使用 map。当 map 仍然存在时,存储在其中的值将不会被垃圾回收。

let map = new Map();
let person = {name: "xiaan"};
map.set(person, "person");
// 覆盖引用
person = null;
// 还能访问对象
console.log(map.keys());

与对象一样,map 可以保存键—值对,我们可以通过键访问值。但是对于 map,我们必须使用 .get() 方法来访问值。

根据 Mozilla Developer Network,Map 对象保存键—值对并记住键的原始插入顺序。任何值(包括对象值和原语值)都可以用作键或值。

map 不同,WeakMap 保存弱引用。因此,如果这些值在其他地方没有被强引用,它不会阻止垃圾回收删除它引用的值。除此之外,WeakMapmap 是相同的。由于弱引用,WeakMap 不可枚举。

对于 WeakMap,键必须是对象,值可以是数字或字符串。

下面的代码片段说明了 WeakMap 的工作原理和其中的方法:

// 创建一个 WeakMap
let weakMap = new WeakMap();
let weakMap2 = new WeakMap();
// 创建一个对象
let ob = {};
// 使用 set 方法
weakMap.set(ob, "Done");
// 你可以将值设置为一个对象甚至一个函数
weakMap.set(ob, ob)
// 可以设置为undefined
weakMap.set(ob, undefined);
// WeakMap 也可以是值和键
weakMap.set(weakMap2, weakMap)
// 要获取值,使用 get 方法
weakMap.get(ob) // Done
// 使用 has 方法
weakMap.has(ob) // true
weakMap.delete(ob)
weakMap.has(ob) // false

在没有其他引用的 WeakMap 中使用对象作为键的一个主要副作用是,它们将在垃圾收集期间自动从内存中删除。

6. WeakMap 的应用

WeakMap 可以用于 web 开发的两个领域:缓存和额外的数据存储。

6.1 缓存

这是一种 web 技术,它涉及到保存(即存储)给定资源的副本,并在请求时返回它。可以缓存函数的结果,以便在调用函数时重用缓存的结果。

让我们来看看实际情况。

let cachedResult = new WeakMap();
// 存储结果的函数
function keep(obj){
	if(!cachedResult.has(obj){
    let result = obj;
    cachedResult.set(obj, result);
  }
	return cachedResult.get(obj);
}
let obj = {name: "xiaan"};
let resultSaved = keep(obj)
obj = null;
// console.log(cachedResult.size); Possible with map, not with WeakMap

如果我们在上面的代码中使用 Map() 而不是 WeakMap(),并且对 keep() 函数有多次调用,那么它只会在第一次调用时计算结果,并在其他时候从 cachedResult 检索结果。副作用是,每当对象不需要时,我们就需要清理 cachedResult。使用 WeakMap(),一旦对象被垃圾回收,缓存的结果就会自动从内存中删除。缓存是提高软件性能的一种很好的方法——它可以节省数据库使用、第三方 API 调用和服务器对服务器请求的成本。通过缓存,请求结果的副本被保存在本地。

6.2 额外的数据存储

WeakMap() 的另一个重要用途是额外的数据存储。想象一下,我们正在建立一个电子商务平台,我们有一个计算访客数量的程序,我们希望能够在访客离开时减少计数。这个任务在 Map 中要求很高,但在 WeakMap() 中很容易实现:

let visitorCount = new WeakMap();
function countCustomer(customer){
  let count = visitorCount.get(customer) || 0;
  visitorCount.set(customer, count + 1);
}
let person = {name: "xiaan"};
// 统计访问人数
countCustomer(person)
// 访客离开
person = null;

使用 Map(),我们必须在客户离开时清除 visitorCount,否则,它将在内存中无限增长,占用空间。但是使用 WeakMap(),我们不需要清理 visitorCount,一旦一个人(对象)变得不可访问,它就会自动被垃圾回收。

7. 小结

在本文中,我们了解了弱引用、强引用和可达性的概念,并尽可能地将它们与内存管理联系起来。

更多关于JavaScript 弱引用强引用的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JavaScript 弱引用强引用底层示例详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JavaScript 弱引用强引用底层示例详解

这篇文章主要为大家介绍了JavaScript 弱引用强引用底层示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Android 软引用和弱引用详解及实例代码

Android 软引用 和 弱引用 1. SoftReference:软引用-->当虚拟机内存不足时,将会回收它指向的对象;需要获取对象时,可以调用get方法。 2. WeakReference:弱引用-
2022-06-06

PHP中弱引用的示例分析

这篇文章主要介绍PHP中弱引用的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!php的框架有哪些php的框架:1、Laravel,Laravel是一款免费并且开源的PHP应用框架。2、Phalcon,Phal
2023-06-15

智能指针与弱引用详解

智能指针有很多实现方式,android 中的sp 句柄类实际上就是google 实现的一种强引用的智能指针。我没有仔细看android sp 的实现方式,但其基本原理是固定的,现在我们从一个相对简单的例子来看智能指针的实现
2022-11-15

iOS中使用对象的弱引用示例代码

简介我们都知道使用 UIImage imageNamed 创建的 UIImage 对象会被持有(强引用),如果图片太大会占用内存,损耗 APP 的性能,影响用户体验,如果能改造对其的强引用变为弱引用就可以解决问题。 我们可能会有类似上面的场
2022-06-05

Three.js引入Cannon.js及使用示例详解

这篇文章主要为大家介绍了Three.js引入Cannon.js及使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-09

DubboConsumer引用服务示例代码详解

dubbo中引用远程服务有两种方式:服务直连(不经过注册中心)、基于注册中心引用服务,在实际线上环境中我们基本上使用的都是基于注册中心引用服务的方式,下面我们就围绕该方式讲解分析
2023-03-02

JavaScript 深拷贝的循环引用问题详解

如果说道实现深拷贝最简单的方法,我们第一个想到的就是 JSON.stringify() 方法,因为JSON.stringify()后返回的是字符串,所以我们会再使用JSON.parse()转换为对象,这篇文章主要介绍了JavaScript 深拷贝的循环引用问题,需要的朋友可以参考下
2022-12-27

MySQL组合索引(多列索引)使用与优化案例详解

目录1、多列索引2、测试案例及过程2.1 创建一个测试数据库和数据表2.2 添加两个单列索引2.3 查询一条数据利用到两个列的索引2.4 查看执行计划2.5 然后删除以上索引,添加多列索引2.6 再次查询3、多列索引的使用顺序3.1 怎么选
2022-07-04

C++之值传递&指针传递&引用传递的示例详解

这篇文章主要为大家详细介绍了C++中值传递、指针传递和引用传递的定义与使用,文中的示例代码讲解详细,对我们学习C++有一定帮助,需要的可以参考一下
2022-11-13

Mysql索引分类及其使用实例详解

目录mysql的索引分类单列索引创建单列索引的几种方式:唯一索引创建唯一索引的几种方式:联合索引(复合索引)创建联合索引(复合索引)的方式:Mysql的索引类型INDEX | NORMAL 普通索引UNIQUE 唯一索引PRIMARY KE
2022-07-19

编程热搜

目录