重学JS的Map、Set、WeakMap、WeakSet

60 阅读6分钟

Map

Map类似一个高配版的Object

MapObject
默认情况下默认情况下不包含任何键,所有键都是开发人员添加进去的Object原型链上有一些默认的键
键的类型键可以是任意类型数据,就连函数都可以Object的键只能是String或Symbol
键值对个数通过size属性获取Object需要手动计算
性能Map在频繁增删键值对的场景下性能要比Object好Map在频繁增删键值对的场景下性能要比Object好

适用场景

  1. 要添加的键值名和Object上的默认键值名冲突,又不想改名时,可以换用Map。
  2. 需要String和Symbol以外的数据类型做键值时,用Map。
  3. 键值对很多,有需要计算数量时,用Map。
  4. 需要频繁增删键值对时,用Map。

使用

属性

 map.size // 2

方法

 let myMap1 = new Map();
 myMap.set("key1", "value1");
 myMap.set("key2", "value2");
 ​
 let myMap2 = new Map([["key1", "value1"], ["key2", "value2"]]);
 ​
 map.clear(); // 清空
 map.delete('key2'); 
 map.get('key1');
 map.has('key1'); // 是否包含某键名对应的值

遍历

 map.keys(); // 返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键 。
 map.values() // 返回键值的遍历器
 map.entries() // 返回键值对的遍历器
 map.forEach() // 使用回调函数遍历每个成员
 ​
 const map = new Map([
   ['a', 1],
   ['b', 2],
 ])
 ​
 for (let key of map.keys()) {
   console.log(key)
 }
 // "a"
 // "b"
 ​
 for (let value of map.values()) {
   console.log(value)
 }
 // 1
 // 2
 ​
 for (let item of map.entries()) {
   console.log(item)
 }
 // ["a", 1]
 // ["b", 2]
 ​
 // 或者
 for (let [key, value] of map.entries()) {
   console.log(key, value)
 }
 // "a" 1
 // "b" 2
 ​
 // for...of...遍历map等同于使用map.entries()
 ​
 for (let [key, value] of map) {
   console.log(key, value)
 }
 // "a" 1
 // "b" 2

应用

 // Map转为数组
 Array.from(map);// [["key1", "value1"], ["key2", "value2"]]
 [...map]; // [["key1", "value1"], ["key2", "value2"]]
 // 数组转为Map
  new Map(arr)
 // Map转为对象
 let obj = {}
 for (let [k, v] of map) {
   obj[k] = v
 }
 // 对象转为Map
 for( let k of Object.keys(obj)){
   map.set(k,obj[k])
 }
 // map合并数组
 new Map([...new Map([[1, 'one'],[2, 'two'],[3, 'three']]), [2, 'dos']]);
 // map合并map
 new Map([...new Map([[1, 'one'],[2, 'two'],[3, 'three']]), ...new Map([[1, 'uno'],[2, 'dos']])]);// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。
 ​

对比

Object 和 Map 的对比

  • Object 对象有原型, 也就是说他有默认的 key 值在对象上面, 除非我们使用 Object.create(null)创建一个没有原型的对象;
  • Object 对象中, 只能把 StringSymbol 作为 key 值, 但是在 Map 中,key 值可以是任何基本类型(String, Number, Boolean, undefined, NaN….),或者对象(Map, Set, Object, Function , Symbol , null….);
  • 通过 Map 中的 size 属性, 可以很方便地获取到 Map 长度, 要获取 Object 的长度, 你只能手动计算

weakMap 和 Map 的对比

  • 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
  • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
  • 不能遍历,方法有 getsethasdelete

Set

Sap类似一个高配版的Array,很多方面都像是加强的Map

  • 所有元素的只有key没有valuevalue就是key

  • 不允许出现键值重复

    +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复

    undefinedundefined 是恒等的,所以不重复

    NaNNaN 是不恒等的,但是在 Set 中认为 NaNNaN 相等,所有只能存在一个,不重复。

  • 所有的元素都会被自动排序

  • 不能通过迭代器来改变set的值,因为set的值就是键

使用

属性

 let s1 = new Set();                   // Set(0) {}
 // 传入数组,或类数组
 let s2 = new Set([1, 2, 2, 3, 5]);    // Set(4) {1, 2, 3, 5}
 set.size; // 4

方法

 set.add('b').add('a');    // Set(2) {"a", "b"}
 set.clear();    //  Set(0) { }
 set.delete(1);    // true,删除成功
 set.delete(1);    // false,值不存在无法删除
 set.has(1);     // true
 set.has({});    // false  与对象内的"{}"并非同一个对象
 ​
 // 间接使用数组的 map 和 filter 方法
 new Set([...set].map((x) => x * 2)) // {2, 4, 6}
 new Set([...set].filter((x) => x % 2 == 0)) // {2, 4}

遍历

 set.values(); // SetIterator {1, 2, 3, 5}
 set.keys();  // SetIterator {1, 2, 3, 5}
 set.entries(); // 返回键值对的遍历器。
 set.forEach(); // 使用回调函数遍历每个成员
 ​
 for (let item of set.keys()) {
   console.log(item)
 }
 for (let item of set.values()) {
   console.log(item)
 }
 for (let item of set.entries()) {
   console.log(item)
 }
 ​

应用

 let arr1 = [1,2,3];
 let arr2 = [3,4,5];
 let s1 = new Set(arr1); //先去除arr1数组自身的重复项
 let s2 = new Set(arr2); //去除arr2数组自身的重复项
 // 并集
 new Set([...arr1,...arr2]) // {1, 2, 3, 4, 5}
 // 交集
 [...s1].filter((item)=>s2.has(item)); // [3] 
 // 差集
 [...s1].filter((item)=>!s2.has(item)); //[1,2] 这是arr1差arr2的结果是[1,2],如果是arr2差arr1就是[4,5]
 // 数组去重
 [...new Set(array)];
 Array.from(new Set(array));
 // Set转为数组
 Array.from(new Set([1, 2, 3, 4, 5]))

对比

Array 和 Set 对比

  • ArrayindexOf 方法比 Sethas 方法效率低下
  • Set 不含有重复值(可以利用这个特性实现对一个数组的去重)
  • Set 通过 delete 方法删除某个值,而 Array 只能通过 splice。两者的使用方便程度前者更优
  • Array 的很多新方法 mapfiltersomeevery 等是 Set 没有的(但是通过两者可以互相转换来使用)

WeakSet 和 Set 对比

  • WeakSet 没有 size 属性
  • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏。
  • WeakSet 不可迭代,因此不能被用在 for-of 等循环中。
  • WeakSet只能存储对象,而Set可以存储任意数据类型

WeakSet的弱引用特性导致它的方法只有以下3个:

  • add(value) 在末尾添加元素
  • delete(value) 删除与这个值相等的元素
  • has(value) 返回是否包含这个值

Map和Set区别

  • 初始化需要的值不一样,Map需要的是一个二维数组,而Set 需要的是一维数组
  • Map 和 Set 都不允许键重复
  • Map的键是不能修改,但是键对应的值是可以修改的;Set不能通过迭代器来改变Set的值,因为Set的值就是键。*

Map 和 Set 数据结构是ES6语法,最大优点就是运行时间少大大提高了性能。

总结

Map

  • 是一种类似于字典的数据结构,本质上是键值对的集合
  • 可以遍历,可以跟各种数据格式转换
  • 操作方法有:setgethasdeleteclear

WeakMap

  • 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
  • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
  • 不能遍历,方法有 getsethasdelete

Set

  • 是一种叫做集合的数据结构
  • 成员唯一、无序且不重复
  • [value, value],键值与键名是一致的(或者说只有键值,没有键名)
  • 允许储存任何类型的唯一值,无论是原始值或者是对象引用
  • 可以遍历,方法有:adddeletehasclear

WeakSet

  • 成员都是对象
  • 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏
  • 不能遍历,方法有 adddeletehas

最后一句

学习心得!若有不正,还望斧正。希望掘友们不要吝啬对我的建议。