Set 和 Map

125 阅读6分钟

前言记录“记忆性丢失”的这种数据结构,体会不一样的捷径api,写这篇文章重点不在记录,而是在于强化吸收!!!

1、Set(集合)

简述: 类似于数组, Set对象是值的集合,你可以按照插入的顺序迭代它的元素。Set 中的元素只会出现一次,即Set 中的值总是唯一的,所以需要判断两个值是否相等(不是我们理解的相等,比如NaN != NaN 但是Set只会出现一次NaN)

控制台的打印,如下:

image.png
  • size :值的个数:类似length,返回值是成员总数
  • add() :尾部添加元素:类似push(),返回值是添加后的set结构
// 数组去重
let set = new Set([2, 1, 1, 3, 3, 2, { a: 1 }, { a: 1 }]);
let res = set.add('a')
console.log(res); // Set(6) { 2, 1, 3, { a: 1 }, { a: 1 }, 'a' } add返回的是添加后的set结构,且Set把引用类型的数据视为不同的值,所以“去不了对象的重”
console.log(set); // Set(6) { 2, 1, 3, { a: 1 }, { a: 1 }, 'a' }

let arr = [1,2]
let res2 = arr.push(3)
console.log(res2); // 3  push返回的是添加的元素

  • has(val) :类似includes(),判断集合中有没有某个元素,返回一个布尔值
  • delete(val) :删掉某个元素,返回一个布尔值
  • clear() :移除所有元素,没有返回值
  • forEach() :跟数组的这个实例方法类似
    new Set(["foo", "bar", undefined]).forEach((val1, val2)=>{
      console.log(val1,val2);//val1和val2都是item, 由于集合对象中没有索引 (keys),所以前两个参数都是Set中元素的值 (values),之所以这样设计回调函数是为了和Map 以及Array的 forEach 函数用法保持一致。
    });
  • entries() :跟数组Array的实例方法类似,返回的iterator遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。
    const arr1 = [1,2,3];
    const iterator1 = arr1.entries();
    console.log(iterator1.next().value);// [0, 1]
    console.log(iterator1.next().value);// [1, 2]

    const arr2 = [1,2,3];
    const iterator2 = arr2.keys();
    console.log(iterator2.next().value);// 1
    console.log(iterator2.next().value);// 2
    
    const arr3 = new Set([1, 2, 3]);
    const iterator3 = arr3.entries();
    console.log(iterator3.next().value); // [1, 1]
    console.log(iterator3.next().value); // [2, 2]

    const arr4 = new Set([1, 2, 3]);
    const iterator4 = arr4.keys();
    console.log(iterator4.next().value);// 1
    console.log(iterator4.next().value);// 2

    const arr5 = new Set([1, 2, 3]);
    const iterator5 = arr5.values();
    console.log(iterator5.next().value);// 1
    console.log(iterator5.next().value);// 2

    const arr6 = new Set([1, 2, 3]);
    const iterator6 = arr6[Symbol.iterator]();
    console.log(iterator6.next().value);// 1
    console.log(iterator6.next().value);// 2
    ...
  • keys() :同values(),因为entres()返回的是[a,a]没有index只有,所以keys()和 values()的用法效果是一样的
  • values() :跟数组的这个实例方法类似,iterator
  • [Symbol.iterator]()效果和values()或者keys()同,也是函数
  • [Symbol.toStringTag] :作为属性使用 返回'Set',更多用法看mdn
  • constructor :构造函数,默认就是Set函数,[Function: Set]

应用场景

  • 去重:
// 数组去重
let arr = [2, 1, 1, 3, 3, 2];
let res1 = [...new Set(arr)];
console.log(res1); // [ 2, 1, 3 ]

// 字符串去重
let str = 'abcabcdddead'
let res2 = [...new Set(str)].join('') // 这里join是个空字符串
console.log(res2);// abcde
  • Set 和 Array互相转化:
    ** Array.from()和拓展运算符 进行 SetArray **
    ** new Set(arr) 进行 ArraySet **
  • 集合的交集、并集等:
let a = [1, 2, 3];
let b = [4, 3, 2];

// 数组,交集
let res1 = a.filter(item => b.includes(item));
console.log(res1); // [ 2, 3 ]

// 集合,交集
let c = new Set(a)
let d = new Set(b)
let res2 = [...c].filter(item => d.has(item));
console.log(res2); // [ 2, 3 ]

1.1、WeakSet(弱集合)

简述: 个人理解,是Set的一个衍生体,区别在于:WeakSet 的成员只能是对象,WeakSet 中的对象都是弱引用,随时可能消失,所以方法和属性较Set都变少了(垃圾回收机制不考虑这个引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存)

控制台的打印,如下:

image.png

弱集合WeakSet的方法属性,与集合Set的用法一致,只是方法属性变少了,且适用对象只能是引用类型的数据。

2、Map(映射)

简述: 类似于对象,对象Object的能用字符串当作键,而映射可以以任何值(对象或者基本类型)都可以作为一个键或一个值。

控制台的打印,如下:

image.png
// 错误用法
const map1 = new Map({a:1})
console.log(map1); // Uncaught TypeError: object is not iterable...

const map2 = new Map([1,2])
console.log(map2); // Iterator value 1 is not an entry object

// 正确用法:任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构
const map3 = new Map([['name', 'liu_xiao'],['age', '23']])
console.log(map3); // Map(2) { 'name' => 'liu_xiao', 'age' => '23' }

const set = new Set([['key',2]])
const map4 = new Map(set)
console.log(map4);// Map(1) { 'key' => 2 }

// 类似实现:
let arr  = [['name', 'liu_xiao'], ['age', '23']]
const map5 = new Map()
arr.forEach(([key, value])=>{
 map5.set(key,value)
})
console.log(map5); // Map(2) { 'name' => 'liu_xiao', 'age' => '23' }

// 串联使用,映射Map中的set,如同Set集合中的add也可以链式使用
const map6 = new Map();
map6.set(1, 'aaa').set(2, 'bbb');
map6.get(1) // "bbb"
console.log(map6); // Map(2) { 1 => 'aaa', 2 => 'bbb' }
  • set(key, value) :对象是通过[]来设置键,映射必须用set键值对的形式设置值,不然会发生异常,返回 Map 对象
  • size :返回 Map 对象中的键值对数量
  • get(key) :得到键值对中的键值value,返回与 key 关联的值
  • has(key) :类似'xxx' in obj,判断映射中有没有某个键名,返回一个布尔值
  • clear() :移除所有元素,没有返回值(或者说返回undefined)
  • delete(key) :删掉指定的键值对,返回一个布尔值
  • forEach() :跟数组的这个实例方法类似
let map = new Map([['name', 'liu_xiao']])
map.forEach((value, key, map)=>{
  console.log(value, key, map);// liu_xiao name Map(1) { 'name' => 'liu_xiao' }
})
  • entries() :跟数组Array的实例方法类似,返回的iterator遍历器,同时包括键名和键值,返回一个新的包含 [key, value] 对的 Iterator 对象
const map1 = new Map([['name', 'liu_xiao'], ['age', 23]]);
const iterator1 = map1.entries();
console.log(iterator1.next().value); // [ 'name', 'liu_xiao' ]
console.log(iterator1.next().value); // [ 'age', 23 ]

const map2 = new Map([['name', 'liu_xiao'], ['age', 23]]);
const iterator2 = map2.keys();
console.log(iterator2.next().value);// name
console.log(iterator2.next().value);// age

const map3 = new Map([['name', 'liu_xiao'], ['age', 23]]);
const iterator3 = map3.values();
console.log(iterator3.next().value);// liu_xiao
console.log(iterator3.next().value);// 23

const map4 = new Map([['name', 'liu_xiao'], ['age', 23]]);
const iterator4 = map4[Symbol.iterator]();
console.log(iterator4.next().value);// [ 'name', 'liu_xiao' ]
console.log(iterator4.next().value);// [ 'age', 23 ]
  • keys() :跟数组Array的keys()类似
  • values() :跟数组Array的values()类似
  • [Symbol.iterator]()效果和entries()同,和Set集合有区别,也是函数
  • [Symbol.toStringTag] :作为属性使用 返回'Map',更多用法看mdn
  • constructor :构造函数,默认就是Map函数,[Function: Map]

应用场景

  • Map 和 Array互相转化:
    ** Array.from()和拓展运算符 进行 SetArray **
    ** new Map(arr) 进行 ArraySet **  
  • Map 和 Object互相转化:
** 进行 MapObject **
function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

** 进行 ObjectMap **

function objToStrMap(obj) {
  let strMap = new Map();
  for (let k of Object.keys(obj)) {
    strMap.set(k, obj[k]);
  }
  return strMap;
}

objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}

let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));
console.log(map); // Map(2) { 'a' => 1, 'b' => 2 }

2.1、WeakMap(弱映射)

简述: 个人理解,是Map的一个衍生体,区别在于:WeakMap 只接受对象作为键名( null 除外), WeakMap 的键名所指向的对象,不计入垃圾回收机制。

控制台的打印,如下:

image.png

弱映射WeakMap的方法属性,与集合Map的用法一致,只是方法属性变少了,且适用对象只能是引用类型的数据。

WeakRef 对象

MDN解释:对于 WeakRef 对象的使用要慎重考虑,能不使用就尽量不要使用,这个方法还在完善且似乎还不能用,现阶段没必要投入学习成本。

let target = {};
let wr = new WeakRef(target);

let obj = wr.deref();
if (obj) { // target 未被垃圾回收机制清除
  // ...
}