【费曼学习法-给女朋友讲ES6】Map 和 Set 数据结构(理解+面试)

1,234 阅读9分钟

前言

大家好,我是 「艾伦」 ,是一名刚进入职场不久的前端er。为了给正在学习前端的女朋友排除万难,笔者决定开始一个全新专栏的更新,该专栏主要围绕经典的费曼学习法用最容易理解的话教会前端小白前端学习中的技术点。因此,该专栏同样适用于正在学习前端基础知识的你

这里会持续更新(要相信爱情的力量😝,欢迎监督催更),如果对你有帮助的话,欢迎关注!!!

当然,如果你觉得文章哪些点还不是很清晰,也欢迎在评论区讨论,期待和你一起成长~

正文

基础知识

  • Map 和 Set是什么? Map 和 Set 是ES6中提出的两种新的数据结构,可以通过类比对象 和 数组来学习

    (温馨提示:注意区分 Map数据结构 和 JavaScript的遍历方法 map )

  • Map 和 Set 的特点(根据obj 和 array 对比学习)

  • Map 和 Set 怎么用?

  • WeakMap 和 WeakSet 的特点 (根据Map 和 Set 对比学习)

常见面试题

  • 你在自学和工作中一般在什么场景下使用map?
  • WeakMap的弱引用能解释一下吗?弱引用的是键还是值,如果弱引用对象被回收,WeakMap里的引用也会消失

WeakMap

是什么东西?

和map一样,用于生成键值对的集

和map的区别?

在使用上的区别:

  • weakmap只接受对象作为键名(不包括null)
  • weakmap的键名所指向的对象,不计入垃圾回收机制(这点和weakset相同,就是垃圾回收机制不会受这个引用的影响)

注意:进行弱引用的是键名,键值还是正常引用的

在API上的区别:

  • 没有遍历操作(和weakset一样)所以没有keys、values、entries遍历方法 只有get、set、has、delete方法可用

为什么要设计出这个东西?

*问题:*当我们想要在对象上存放一些数据,这会导致我们对这个对象进行了引用。例子如下:

//仅对e1,e2 两对象进行了文字说明,造成了引用
const arr = [
  [e1, 'foo 元素'],
  [e2, 'bar 元素'],
];

然而当我们在别的逻辑不需要这个对象了,需要将他删除的时候,我们还要手动删除掉这个引用,否则垃圾回收机制不会释放这个对象占用的内存,造成内存泄漏。

weakmap就是为解决这个问题而生的。它的键名的引用是弱引用,垃圾回收机制不会考虑这个引用。

使用场景

*最大的用途:解决垃圾回收的问题 当你想要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用weakmap 经典使用场景:

  • 在网页的DOM元素上添加数据,就可以使用weakmap。当DOM被清除,weakmap保存的这个键名也会消失
  • 部署私有属性:
const _counter = new weakMap()
const _action = new WeakMap()
class Countdown{
    constructor(counter,action){
        _counter.set(this,counter)
        _action.set(this,action)
    }
}

在上述代码中,在构造器中初始化属性,对实例this是弱引用。好处:如果删除实例,它们也会随之消失,不会造成内存泄漏

Map

特点

  • 任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。第一个元素作为key。第二个元素做value
  • 在判别是否为相同属性的规则和Set一致:
    1. 对引用数据类型,始终认为时两个不同的键或值。
    2. 对普通数据类型,全等则视为相同

实例的属性和操作方法

  • size属性:返回Map结构中成员总数
  • set(key,value):设置键名key对应的值为value。然后返回整个map结构。如果key已经有值,则键值会被更新。可以采用链式写法
  • get(key):读取key对应的键值并返回。如果找不到就返回undefined
  • has(key):判断键是否在当前Map对象中
  • delete(key):删除某个键。成功则返回true,反之,返回false
  • clear():清除所有成员,没有返回值

遍历方法 —— 和Set一致

Map和其他数据结构的转换

  • Map转为对象:直接循环拷贝一次即可,如果有非字符串键名,会自动转为字符串
  • 对象转为Map:Object.entries(obj)(就是以[key , value]形式遍历所有项)
  • Map转JSON:
    1. Map键名都是字符串,转为对象JSON
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));

WeakSet

  • 和Set一样,也是不重复的值的集合

和Set的区别:

  • WeakSet的成员只能是对象,不能是其他类型的值。

  • WeakSet没有size属性,并且不能遍历它的成员。是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。

  • 其次,WeakSet中的对象都是弱引用,也就是说垃圾回收机制回收时不会考虑该对象是否还存在WeakSet之中。 原因: 因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,立即回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet没有这个问题。 使用场景: 因此,WeakSet适合存放一组对象,以及存放跟对象绑定的信息。只要对象在外部消失,它在WeakSet里面的引用就会自动消失。 注意

  • 1、WeakSet的成员不适合引用,因为它随时可能消失.

  • 由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

语法

  • WeakSet可以接受任何具有Iterable接口的对象作为参数.如果接收的参数的成员是引用数据类型 , 那么它们会自动成为WeakSet实例对象的成员
  • WeakSet的方法
    1. add(value):向实例中添加一个新成员
    2. delete(value):清除实例的指定成员
    3. has(value):返回一个布尔值,表示该值是否在实例之中

Set

  • 是什么? ES6提供的一种新的数据结构,类似于数组,成员的值是唯一的,没有重复的值。
  • 怎么用?
  1. Set是一个构造函数,用来生成Set数据结构
  2. 可以通过add()方法向Set结构加入成员,但不会添加重复的值
  3. Set函数可以接受一个数组(或具有iterable接口(即可以用来遍历的数据类型)的其他数据结构)作为参数,用来初始化 注意:
  • 向Set加入值时,不会发生类型转换。所以add(5)add('5')是不一样的
  • Set内部判断两个值是否相同,有自己的算法规则,类似于(===)类型和数据都相等,但还是有特殊情况的:
  • 在Set内部认为NaN等于自身。即:add(NaN)后,再一次add(NaN)被认为是重复的,set最终只会有一个NaN
  • 在Set中,两个对象总是不相等的。即add({})后,再add({}),set.size为2

问题:为啥{} === {} 是false 引用数据类型比较的是引用,而不是值。两个不同的对象,引用地址显然不同,故为false

Set实例的方法和属性

  • 属性
    1. Set.prototype.constructor 构造函数 = Set
    2. Set.prototype.size 返回Set实例的成员总数
  • 方法
    • 操作方法
    1. add(value) 添加默认值,返回Set结构本身
    2. delete(value) 删除某个值,返回布尔值,表示是否删除成功
    3. has(value)返回boolean值,表示该值是否是Set的成员
    4. clear():清除所有成员,没有返回值
    • 遍历方法 注:Set的遍历顺序就是插入顺序。
    1. keys()
    2. values()
    3. entries():返回的遍历器,同时包含键名和键值,输出为一个数组,两个成员完全相同 注:前三种方法返回的都是遍历器对象,由于Set没有键名,只有键值,所以keys和values的行为完全一致》
    4. forEach():和数组的一样。第一个参数:回调函数。回调的参数为键值、键名、集合本身。第二个参数:绑定处理函数内部的this对象
  • 注意:
  1. 因为Set的实例默认遍历,这意味着可以忽略values方法,直接用for...of循环遍历Set
  2. 使用set进行数组去重:1、Array.from 可以将Set结构转为数组。所以Array.from(new Set(array))是一种数组去重的方法。 2、利用扩展运算符和Set结构结合: [...new Set(arr)],就能达到去重的效果
  • Set的重要应用
    1. 很容易实现并集、交集、差集
    let a =newSet([1,2,3]);
    let b =newSet([4,3,2]);
      // **并集**
    let union=**newSet([...a,...b]);**
      // Set {1, 2, 3, 4}
      // **交集**
      let intersect =**newSet([...a].filter(x => b.has(x)));**
      // set {2, 3}
      // (a 相对于 b 的)**差集**
        let difference =**newSet([...a].filter(x =>!b.has(x)));**
      // Set {1}
    
  1. 如果想在遍历操作中,同步修改原来的Set结构,没有直接的方法,但有两种间接的方法:
// 方法一:通过map返回一个新的Set,最后赋值给原来的Set
 let set=newSet([1,2,3]);
 set=newSet([...set].map(val => val *2));
// set的值是2, 4, 6
// 方法二:
 let set=newSet([1,2,3]);
 set=newSet(**Array.from(set, val => val *2)**);
// set的值是2, 4, 6

另外: Array.from

  • 参数
    1. 想要转换为数组的伪数组对象或可迭代对象(set\map\string\array都行)
    2. 回调函数。新数组中的每个元素都会执行该回调
    3. 上面回调函数执行时的this对象
  • 返回值 一个新的数组实例

总结

介绍下Set、Map、WeakSet、WeakMap的区别?

  • Set和WeakSet 以及 map和weakMap:
  1. Set的成员可以是任何数据结构,而weakset的成员只能是对象
  2. 在Set中对对象的引用都是正常引用,而weakset是弱引用,这种引用不会影响垃圾回收机制
  3. weakset不能遍历它的成员。weakset没有size属性和keys、values、entries这三个遍历方法
  • set 和 map:
  1. set实例是一个不重复的值的集合,键名和值一致
  2. map实例是一个键值对得集合,任何得数据结构都能作为键名的对象

结语

备战秋招,这是复盘阶段系列文章第一篇。欢迎点赞持续关注,一起冲!!!