引言
今天,我们要聊的是在js里面两种新的数据结构Set和Map,set是一种非常有趣的东西,既有点像数组,又有点像对象,但是你说它是数组或则对象嘛,它又不是。下面,让我们来一起探索一下吧!
Set的简单介绍
Set 是一种集合数据结构,用于存储唯一值的集合。这意味着在一个Set中,任何值都只能出现一次,重复的值会被自动忽略。Set类似于数组,但与数组不同的是,它确保了所有元素的唯一性,并且没有索引。Set适合用于当你需要从数组或其他可迭代对象中过滤掉重复项,或者当你需要检查一个值是否存在于集合中时。
上面介绍的时候说Set中,任何值都只能出现一次,重复的值会被自动忽略
const s = new Set([1, 2, 3, 3])
console.log(s)
思考一下,下面会打印什么呢?相信这个时候,你心中应该有答案了吧
没错,这里面会把重复的数字3给去掉,也就验证了上面那一句话
当我们了解一种新的数据结构时,首先,我们要来了解它自带的方法
增,删,改,查,是最基本的
const s = new Set([1, 2, 3, 3]) //创建一个Set
s.add(2) //往Set里面增加一个数
s.has(1) //判断s是否含有某个数
s.delete(2) //删掉某个数
s.clear() // 清除s里面的所有东西
思考:怎么去除数组内重复的数?
当我们遇到一个算法问题的时候,一般有两种方法,第一种:自己手戳一个,第二种:看看这种数据结构有没有自带的方法,能够实现,如果没有自带的方法,思考一下,可不可以借用其他的数据结构来实现。
显然,数组里面没有清除数组内重复元素的方法>
刚好,今天我们学了一个Set的数据结构,我们是不是只要把数组转化为Set就行了呢?
const arr1=[1,2,3,4,4]
const arr2=[...new Set(arr1)]
来解释一下这段代码
-
const arr1 = [1, 2, 3, 4, 4];:定义了一个名为arr1的数组,其中包含一些重复的数字。 -
const arr2 = [...new Set(arr1)];:这部分代码执行了以下几个步骤:new Set(arr1): 首先,通过Set构造函数将arr1转换为一个集合。在这个过程中,由于集合(Set)自动去除了所有重复的元素,所以结果中只保留了每个元素的唯一副本。[... ]:这是扩展运算符(spread operator),它能够将一个可迭代对象(在这里是Set)展开为一系列用逗号分隔的值。- 结合上述两点,
[...new Set(arr1)]就创建了一个新的数组arr2,这个新数组包含了来自arr1的所有唯一值,即去重后的结果。
因此,执行完这段代码后,arr2将会是一个数组,内容为[1, 2, 3, 4],这正是从arr1中去除了重复元素的结果。
两行代码就搞定,是不是很简单
问题来了,怎么访问Set里面的值呢? 传统思想,你可能会
const mySet = new Set([1, 2, 3]);
console.log(mySet[0]); // undefined
但是如果直接访问下标,会显示undefined
在JavaScript中,Set数据结构并不支持直接通过下标(索引)来访问元素,因为Set是用来存储唯一值的集合,它的内部并没有维持元素插入顺序的索引。
使用迭代器进行循环:
let mySet = new Set([1, 2, 3]);
for (let value of mySet) {
console.log(value);
}
for (let value of mySet) { ... }:这是一个for...of循环,用于遍历可迭代对象(在这里是Set实例mySet)。在每次循环中,变量value会被赋予集合中的下一个值。
面试巨坑:WeakSet
这里用一个例子来解释
首先,我们得知道弱引用和强引用 --弱引用
1.一个对象存在了其他结构种(非WeakSet) ,当后续再也没有其他引用对象,那么这个对象的内存就不会被回收
2.一个对象存在了其他结构中,当后续存在WeakSet对它的引用,该对象内存依然会被回收 当WeakSet引用到这个对象,对不起,直接进行回收,WeakSet就相当于炸弹,一碰到别人,别人就爆掉 WeakSet用到了,垃圾回收机制就会直接回收
// 创建一个对象
let user = { name:"牛哥" };
// 创建一个WeakSet并添加user对象
let usersSet = new WeakSet();
usersSet.add(user);
// 删除对user对象的强引用
user = null;
// 此时,如果垃圾回收机制运行,user对象可能会被回收,
// 因为它仅存在于WeakSet中,没有其他强引用了。
这样你可能听不懂,举个例子吧
从前,有个叫“牛哥”的"大爷",是电脑的垃圾回收器,负责清理不再需要的房间以腾出空间。
- 门边的鞋子(强引用) :就好比一个对象被你的代码中的一个变量直接引用着,就像家人明确表示这个房间还在用,门边放了鞋子作为标记。因此,"聪明的牛哥"(垃圾回收器)知道这个房间还被需要,不会清理它。
- 楼道里贴了便利贴(WeakSet的弱引用)的旧物:如果一个对象只被
WeakSet引用,这就像是楼道里贴了一张便利贴,标记某个房间的物品为待观察,但没有实际的人(强引用)声明占有这个房间。这种情况下,"牛哥"在清理时会检查这些便利贴标记的物品,如果发现真的没有人来认领(没有其他强引用关联),他就会清理这些房间,因为它们已经没有实际的使用者了。
总结起来,WeakSet中的对象就像是那些被标记为可放弃的资源,一旦它们在程序的其他部分失去了所有的强引用(即没有实际的使用场景),"大爷"(垃圾回收机制)就会自动清理这些对象所占用的内存,使得系统保持高效运行。
就这么理解吧,假如你定义了一个对象,你只要把WeakSet(弱引用)添加到这个对象上去,这个对象就相当于朝廷下了通缉令,这个人已经没有任何用处,必须得处死(也叫做回收)
Map的简单介绍
Map是一种内置对象,它提供了键值对的存储结构,是一种新的对象
介绍一些方法
const m=new Map() //其他任意类型作为key,弥补其他传统的不足
const o={age:18}
m.set(o,[1,2,3]) //对象为key,value为一个数字
m.get(o) //读取key为o
m.delete(o)
m.has() //判断里面是否包含....
m.size() //对象里面有几个key
m.keys() //拿到有哪些key
m.values() //拿到里面有哪些value
m.entries()
let myMap = new Map(); // 创建一个空的Map实例
// 添加键值对
myMap.set('name', '牛哥'); // 添加键值对 ('name', '牛哥')
// 获取值
console.log(myMap.get('name')); // 输出: 牛哥
// 检查键是否存在
console.log(myMap.has('name')); // 输出: true
// 更新已存在的键的值
myMap.set('name', '牛二哥'); // 现在 'name' 对应的值是 '牛二哥'
// 再次获取值,验证更新
console.log(myMap.get('name')); // 输出: 牛二哥
// 使用迭代器遍历Map
for (let [key, value] of myMap) {
console.log(`${key}: ${value}`);
}
// 输出:
// name: 牛二哥
// 清空Map
// myMap.clear();
// 删除特定键值对
myMap.delete('name');
// 检查键是否已被删除
console.log(myMap.has('name')); // 输出: false, 因为刚刚删除了
Object可以创建一个对象,Map()也可以,两者有什么区别呢
- Object:键必须是字符串或Symbol类型。如果使用非字符串或非Symbol类型的键,它们会被转换成字符串。例如,数字键会被转换成对应的字符串形式。
- Map:键可以是任何值,包括对象。这意味着你可以在Map中使用对象作为键,而不用担心它们会被转换成字符串。
let obj={
a:1
}
当你写了这样一段代码的时候,v8会把a看作是字符串'a'1,但是Map 键可以是任意类型:包括对象,提供了更大的灵活性。
以下,就是对Set和Map的介绍
制作不易,感谢支持