Set和Map数据结构介绍及使用场景分析

1. Set结构

特征介绍:
1. 类似于数组,没有重复的值
2. 本身是一个构造函数
3. 可以接受一个iterable对象(数组、类数组、字符串...)
4. 有序
5. 键值相等

const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
  console.log(i);
}
// 2 3 5 4

// 去除数组的重复成员(特例: 两个NaN会认为是重复,===判断相等)
[...new Set([1,2,1,'2',NaN,{},NaN,{},4])]
// [1, 2, "2", NaN, {…}, {…}, 4]

// 字符串去重
[...new Set('ababbc')].join('')
// "abc"
属性和方法

size、add()、delete()、has()、clear()

let s=new Set([1,2,3,2]);
s.size //3 相当于数组的length,去重后的结果
s.add(4) //添加一个值,返回 Set 结构本身 可以链式调用
s.delete(2) //true  删除某个值,返回一个布尔值,表示删除是否成功。
s.has(2) //false 返回一个布尔值,表示该值是否为Set的成员
s.clear() // 清除所有成员,没有返回值
遍历 (键值相等)

keys()、values()、entries()、forEach()

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

for (let x of set) {
  console.log(x);
}
// red
// green
// blue

let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
使用

数组去重

let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];//使用...
let unique2 = Array.from(new Set(arr));//使用Array.from()

并集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

交集

let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

2. WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。
它与 Se的区别:

  1. WeakSet 的成员只能是对象,而不能是其他类型的值
const ws = new WeakSet();
ws.add(1)
//以上代码会报错
ws.add({a:1})

  1. WeakSet对象只有add delete has 没有clear方法和size属性
ws.size  // undefined
ws.clear() //抛错
  1. WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
    只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失
const ws = new WeakSet();
let obj1={a:1};
let obj2={b:1};
ws.add(obj1);
ws.add(obj2);
obj2=null;
console.log(ws.has(obj1))  // true
console.log(ws.has(obj2))  // false 引用被释放调了
console.log(ws)
//ws引用中只有{a:1} 
//删除obj2引用的时候,不用考虑ws对象,页不用考虑内存泄漏
image.png

3. Map结构

介绍:
  1. 它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括null、undefined、对象、数组、正则。。。)都可以当作键
  2. 同一个键多次赋值,后面的值将覆盖前面的值
  3. 只有对同一个对象的引用,Map 结构才将其视为同一个键(Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。)
const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"

// Map构造函数接受数组作为参数,实际上执行的是下面的算法。

const items = [
  ['name', '张三'],
  ['title', 'Author']
];
const map = new Map();
items.forEach(
  //二级数组的第0个值作为key,第一个值作为value
  ([key, value]) => map.set(key, value)
);
属性和方法

size、set(key,value)、 get(key)、 has(key)、 delete(key)、 clear()

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);
let obj={a:1}
map.size // 2
map.set(obj,10)
//0: {"name" => "张三"}
//1: {"title" => "Author"}
//2: {Object => 10}
map.get(obj) // 10
map.has(obj) // true
map.size // 3
map.delete(obj) // true
map.clear() // undefined
console.log(map) // {}
遍历(map也是有顺序的)

keys()、values()、entries()、forEach()

const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"
map.forEach(function(value, key, map) {
  console.log("Key: %s, Value: %s", key, value);
});
应用(if...else...的优雅写法)

复杂的判断处理,使用if...else

const onButtonClick = (status,identity)=>{
  if(identity == 'guest'){
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }else if(identity == 'master') {
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }
}

升级为switch-case,还是比较麻烦的

const onButtonClick = (status, identity)=>{
  if(identity == 'guest'){
    switch (status){
      case 1:
        //do sth
        break
      case 2:
        //do sth
         break
      //......
    default:
      //do sth
      break
    }
  }else{
    switch (status){
      case 1:
        //do sth
        break
      case 2:
        //do sth
         break
      //......
    default:
      //do sth
      break
    }
  }
}

升级为map写法(也可以用object对象定义actions,效果一样)

const actions = new Map([
  ['guest_1', ()=>{/*do sth*/}],
  ['guest_2', ()=>{/*do sth*/}],
  ['guest_3', ()=>{/*do sth*/}],
  ['guest_4', ()=>{/*do sth*/}],
  ['guest_5', ()=>{/*do sth*/}],
  ['master_1', ()=>{/*do sth*/}],
  ['master_2', ()=>{/*do sth*/}],
  ['master_3', ()=>{/*do sth*/}],
  ['master_4', ()=>{/*do sth*/}],
  ['master_5', ()=>{/*do sth*/}],
  ['default', ()=>{/*do sth*/}],
])

const onButtonClick = (identity,status)=>{
  let action = actions.get(`${identity}_${status}`) || actions.get('default')
  action.call(this)
}

发挥map的特性(用对象作为key,适用于多元判断)

const actions = new Map([
  [{identity:'guest',status:1},()=>{/*do sth*/}],
  [{identity:'guest',status:2},()=>{/*do sth*/}],
  //...
])

const onButtonClick = (identity,status)=>{
  let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status))
  action.forEach(([key,value])=>value.call(this))
}

如果存在有些状态对应的处理方式是一样的,那么map对象会显得有些啰嗦
这里可以使用正则类型数据作为map的key

const actions = ()=>{
  const functionA = ()=>{console.log('a')}
  const functionB = ()=>{console.log('b')}
  const functionC = ()=>{console.log('c')};
  const functionD = ()=>{console.log('d')}
  return new Map([
    [/^guest_[1|4|6]$/,functionA],
    [/^guest_[2|3|5]$/,functionB],
    [/^master_[1-4]$/,functionC],
    [/^master_[5-7]$/,functionD],
    //...
  ])
}
const ac=(identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}
ac('guest',2)// b
ac('guest',1)// a
ac('master',2)// c
ac('master',7)// d

---------转自JavaScript 复杂判断的更优雅写法--------

4. WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。
区别

  1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制(类似于WeakSet)
  3. 是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性
const wm=new WeakMap();
let list=[1,2,3];
let val=[4,5];
wm.set(list,val);
list=null;
wm.get(list);// undefined
应用(vue3.0数据劫持实现)

vue3.0中的数据监听原理简析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354