前言
最近在学习es6的时候,了解到了weakSet和weakMap这两种数据结构,并且发现了它们对对象的引用方式都为弱引用,那么什么是弱引用呢?是不是还有强引用呢?在给大家聊这个问题之前,咱们先来聊聊set和map身上的一些方法和知识点。
正文
Set
一、set实例的创建
ini
const s = new Set();
二、set身上的方法
- add(value) : 向
Set
集合中添加一个元素。如果该值已存在,则不会重复添加。- delete(value) : 从
Set
集合中删除指定的元素。如果元素存在并被成功删除,返回true
;否则,返回false
。- clear() : 清空
Set
集合中的所有元素。- has(value) : 检查
Set
集合中是否包含指定的元素,返回一个布尔值。- size : 属性而非方法,返回
Set
集合中元素的数量。- values() , keys() , entries() : 这些方法用于获取集合中的元素。在
Set
中,由于每个元素都是键也是值,所以keys()
和values()
返回相同的结果,而entries()
返回的是键值对数组,每个键值对中键和值是相同的。- forEach() : 对
Set
中的每个元素执行一次提供的函数。- [Symbol.iterator] : 使
Set
可迭代,可以使用for...of
循环遍历集合中的元素。
给大家来举个例子:
scss
const s = new Set();
s.add(1);
s.add(2);
console.log(s);
console.log(s.keys()); // 键名
console.log(s.values()); // 值
console.log(s.entries()); // 键值对
可以看到在Set
中,由于每个元素都是键也是值,所以keys()
和values()
返回相同的结果,而entries()
返回的是键值对数组,每个键值对中键和值是相同的。
三、set的遍历
咱们还可以通过传入数据的方法来初始化Set实例,用for...of对其进行遍历
javascript
const s = new Set([1, 2, 3, 3]);
for (let item of s) {
console.log(item);
}
这里由于键名中有两个3,同属于一个键名,因此放入一个键名中。
四、可以使用[...new set(arr)]来对数组去重
操作如下:
ini
const arr = [1, 2, 3, 4, 4, 3, 2, 1];
const arr2 = [...new Set(arr)];
console.log(arr2);
可以看到使用[...new set(arr)]可以去除数组中的重复元素。
Map
JavaScript中的Map
对象是一种可存储键值对的数据结构,其中的键可以是任何值(包括对象)。
一、Map实例的创建
javascript
const m = new Map(); // 可以用任意类型做key
二、Map身上带有的方法
- set(key, value) : 向
Map
中添加或设置键值对。如果键已经存在,则会更新对应的值。- get(key) : 返回与指定键关联的值,如果不存在,则返回
undefined
。- has(key) : 判断
Map
中是否存在指定的键,返回布尔值。- delete(key) : 删除与指定键关联的键值对,如果删除成功返回
true
,否则返回false
。- clear() : 清空
Map
中的所有键值对。- size : 属性,返回
Map
中键值对的数量。- keys() : 返回一个新的迭代器对象,包含了
Map
中所有键的迭代器。- values() : 返回一个新的迭代器对象,包含了
Map
中所有值的迭代器。- entries() : 返回一个新的迭代器对象,包含了
Map
中所有键值对([key, value])的迭代器。- forEach(callbackFn[, thisArg]) : 对
Map
中的每个键值对执行提供的函数。callbackFn
接收三个参数:当前值、当前键和Map
本身。- [Symbol.iterator] : 使
Map
可迭代,可以用for...of
循环遍历键值对。
它的遍历方法和set差不多,这里我就不在叙述啦,接下来跟大家聊聊weakSet和weakMap,以及js中的强引用和弱引用。
weakSet
JavaScript中的Set
和WeakSet
都是用于存储唯一值的集合,但它们之间存在一些关键性的差异:
-
存储内容:
Set: 可以存储任何类型的值,包括原始值(如字符串、数字)和对象引用。
WeakSet : 只能存储对象引用,不能存储原始值。这意味着你不能在
WeakSet
中放入字符串、数字、布尔值等非对象类型。 -
引用强度:
Set : 对其包含的元素保持强引用,这意味着只要
Set
存在,它所包含的元素就不会被垃圾回收,即使这些元素在其他地方没有引用。WeakSet : 对其包含的对象持有弱引用。这意味着如果对象在
WeakSet
之外没有其他引用,那么垃圾回收机制可以回收这些对象,即使它们还在WeakSet
中。这有助于避免内存泄漏,特别是当用来跟踪临时的对象状态时。 -
迭代与大小:
Set : 提供了
size
属性来获取集合的大小,并且可以使用forEach
、for...of
等方法来遍历集合中的所有元素。WeakSet : 没有
size
属性,也无法进行遍历操作(没有forEach
、keys
、values
、entries
等方法)。这是因为它的设计目的是为了隐私和垃圾回收的便利,不允许外部直接访问其内部状态或元素。 -
方法集:
Set : 提供了
add
、delete
、has
、clear
以及迭代方法。WeakSet : 只提供了
add
、delete
和has
方法,没有clear
方法,因为无法遍历其内容来清除,因为弱引用的特性会自动处理不再被使用的对象。
第二点可能有点不太好理解,这里我给大家来解释一下第二点,
什么是强引用呢?
**强引用 **
表示变量对一个对象的直接持有。当一个对象被一个变量通过赋值方式引用时,就形成了对该对象的一个强引用。在v8引擎中,可以理解为有一个指针从变量指向那个引用地址,如果该地址被指向,那么这个引用就不会被JS垃圾回收机制当作"垃圾"回收掉。而如果我们又将该引用分配给了一个新的变量,那么在栈内存当中,又会为这个新变量和该引用创建一个新的指针使他们连接在一起。
给大家举个例子:
ini
let user = { name: "张三" }
let user2 = user
在这个例子中,将引用{name:"张三"}
赋值给了user,这个操作在v8引擎中会存在这样一个过程:
- 在堆中创建对象
{name:"张三"}
- 在栈中创建变量
user
指向堆区中的对象{name:"张三"}
- 最后又创建一个变量
user2
,并将user赋值给user2,在这里的赋值其实是将user
的引用地址赋给user2
,使得user
和user2
同时指向堆区的{name : "张三"}
在上述过程中,对象被一个变量通过赋值方式引用时则为对象强引用。
什么是弱引用?
弱引用
是一种特殊类型的引用,它允许垃圾回收器在没有其他强引用存在的情况下回收被引用的对象,即使还有弱引用指向该对象。
咱们今天要聊的WeakSet
和WeakMap
两种类型,就是典型的弱引用,它们只接受对象作为其元素,并且对这些对象保持弱引用。这意味着如果一个对象仅被WeakSet
或WeakMap
引用,即使WeakSet
或WeakMap
仍然保持对它的引用,它也可以被垃圾回收。
在给大家以weakset为例举个例子:
javascript
let user = { name: "张三" }
let user2 = user
// 来创建一个WeakSet实例
const user3 = new WeakSet()
// 为实例user3添加一个数据:user2
user3.add(user2)
console.log(user3.has(user2))
咱们首先创建两个变量user,user2强引用同一个对象{name:"张三"},接着创建weakset实例弱引用对象{name:"张三"},同样是上面那个图,咱们可以理解为user3对{name:"张三"}的弱引用理解为虚线指向,在v8引擎中,强引用与对象之间存在一种强效的连接,而弱引用与对象之间的连接并不被V8所认可。也就是说弱引用并不知道自己被user3
实例所引用,此时垃圾回收机制也不知道该引用被user3
实例所引用。那么在此时如果所有的对{name:"张三"}
的强引用消失时,那么该引用就会被js垃圾回收机制销毁,即使user3
还存在对该对象的引用。
比如说咱们再将user和user2的引用改为null时:
ini
let user = { name: "张三" }
let user2 = user
// 来创建一个WeakSet实例
const user3 = new WeakuserSet()
// 为实例user3添加一个数据:user2
user3.add(user2)
console.log(user3.has(user2))
user = null
user2 = null
console.log(user3.has(user2))
此时可以理解为user
、user2
与{name:"张三"}
之间的强效连接断开,那么这个时候user3
中对{name:"张三"}
的引用会发生什么呢?
这时user
、user2
对{name:"张三"}
的强引用全部断开,此时{name:"张三"}
不存在其他的强引用,即使在user3
中仍然存在对{name:"张三的"}
的弱引用,垃圾回收机制也会认为{name:"张三"}
不需要了而将它销毁。
WeakMap
的弱引用方式也和WeakSet
的方法一样,这里咱就不说啦
总结:
弱引用:
当它执行完后垃圾回收机制就会回收它,不管后面有没有其他引用指向它。
- 一个对象obj存放在了其他的结构中,当后续存在其他对象引用这个对象,那么这个对象的内存就不会被回收。
- 一个对象obj存放在了其他的结构中,当后续只存在WeakSet对它的引用,该对象的内存依然会被回收。
好啦,今天的分享就到这里啦,希望本文能对你理解强弱引用有新的帮助哈!可以留下一个免费的赞赞嘛 感谢感谢!