聊一聊ES6当中新的数据结构Set与Map

595 阅读7分钟

引言

今天,我们要聊的是在js里面两种新的数据结构Set和Map,set是一种非常有趣的东西,既有点像数组,又有点像对象,但是你说它是数组或则对象嘛,它又不是。下面,让我们来一起探索一下吧!

Set的简单介绍

Set 是一种集合数据结构,用于存储唯一值的集合。这意味着在一个Set中,任何值都只能出现一次,重复的值会被自动忽略。Set类似于数组,但与数组不同的是,它确保了所有元素的唯一性,并且没有索引。Set适合用于当你需要从数组或其他可迭代对象中过滤掉重复项,或者当你需要检查一个值是否存在于集合中时。

上面介绍的时候说Set中,任何值都只能出现一次,重复的值会被自动忽略

             const s = new Set([1, 2, 3, 3])
             console.log(s)

思考一下,下面会打印什么呢?相信这个时候,你心中应该有答案了吧

微信图片_20240528103254.png 没错,这里面会把重复的数字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)]

来解释一下这段代码

  1. const arr1 = [1, 2, 3, 4, 4];:定义了一个名为arr1的数组,其中包含一些重复的数字。

  2. 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的介绍

制作不易,感谢支持