正文

内存和性能管理是软件开发的重要方面,也是每个软件开发人员都应该注意的方面。虽然弱引用很有用,但在 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 弱引用强引用的资料请关注Devmax其它相关文章!

JavaScript 弱引用强引用底层示例详解的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部