JavaScript Set和Map数据结构
JavaScript Set和Map数据结构
文章目录
一.前言
ES6 提供了两种新的数据结构 Set和Map.Set是集合,Map是字典
二.Set
Set是一个构造函数,用来生成Set数据结构,它类似于数组,但是
成员的值都是唯一的,没有重复的
初始化Set可以接受一个数组或类数组对象(或者具有iterable接口的其他数据结构)作为参数,也可以创建一个空的Set
let s1 = new Set();
let s2 = new Set([1, 2, 3, 4]);
let s3 = new Set(document.querySelectorAll('div'));
console.log(s1);//Set(0)
console.log(s2);//Set(4)
1.成员唯一
在Set中成员的值是唯一的,重复的值自动被过滤掉
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3);//{1, 2, 3, 4}
2.属性方法
1. size
返回成员总数
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.size);//去重之后,成员总数为4
2.add()
添加某个值,返回Set结构本身(可以链式调用)
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.add([6,7]))//Set(5) {1, 2, 3, 4, Array(2)}
console.log(s3.add(5));//Set(5) {1, 2, 3, 4, 5}
3.delete()
删除某个值,返回一个bool值,表示是否删除成功
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.delete(4));//true
4.has()
返回一个bool值,表示该值是否为Set的成员
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.has(5));//false
5.clear()
清除所有成员,没有返回值
let s3 = new Set([1, 2, 2, 3, 3, 4])
s3.clear();
console.log(s3);//Set(0) {}
3.无法按下标查询,修改,排序
得益于数据结构Set查找更快速更高效,但是也因为数据结构的内部数据是无序的,无法实现按下标查询,修改,排序等操作
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
console.log(s1[0]);//undefined
console.log(s1['a']);//undefined
4.Set的取值
Set没有类似getter方法,取值怎么取呢?
1.扩展运算符解构赋值
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
var [a, b, c] = [...s1];
console.log(a);//1
console.log(b);//2
console.log(c);//a
var array=[...s1];
console.log(array);//[1, 2, "a", 3, "b"]
2.forEach遍历
使用回调函数遍历每个成员
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
s1.forEach((val, key, set) => {
console.log("val", val);
console.log("key", key);
console.log("set", set);
});
Set结构的键名就是键值(两者同一个值),因此使用forEach遍历时,第一个参数和第二个参数的值永远都是一样
3.for…of遍历
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1) {
console.log(val);
}
可以循环遍历出Set中的每一个值
4.keys()
返回键名的遍历器
由于 Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys
方法和 values
方法的行为完全一致。
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.keys()) {
console.log(val);
}
5.values()
返回键值的遍历器。
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.values()) {
console.log(val);
}
//1
//2
//a
//3
//b
6.entries()
返回键值对的遍历器
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.values()) {
console.log(val);
}
//[1, 1]
//[2, 2]
//["a", "a"]
//[3, 3]
//["b", "b"]
5.Array和Set的对比
-
Array
的indexOf
方法比Set
的has
方法效率低下 -
Set
不含有重复值(可以利用这个特性对数组进行去重) -
Set
通过delete
方法删除某个值,而Array
只能通过splice
,两者的使用方便程度,前者更优. -
Array
的很多新方法map
,filter
,some
,ervery
等是Set
没有的(但是两者可以互相转换来使用)
6.Set的应用
1.Set转为数组
使用Array.from方法可以将Set结构转为数组
const items = new Set([1, 2, 3, 4, 5])
const array = Array.from(items)
2.数组去重
// 方法一:
var arr1 = [1,2,4,2,3,5,1,3];
var arr2 = [...new Set(arr1)];
console.log(arr2);// [1, 2, 4, 3, 5]
// 方法二:
var arr1 = [1,2,4,2,3,5,1,3];
var arr2 = Array.from(new Set(arr1));
console.log(arr2);// [1, 2, 4, 3, 5]
3.数组的map和filter可以间接用于Set
let s2 = new Set([1, 2, 3, 4]);
s2 = new Set([...s2].map((ele) => {
return ele * 2;
}));
console.log(s2);//Set(4) {2, 4, 6, 8}
let s2 = new Set([1, 2, 3, 4]);
s2 = new Set([...s2].filter((ele) => {
return ele > 2;
}));
console.log(s2)//Set(2) {3, 4}
4.实现并集 (Union)
、交集 (Intersect)
和差集
并集:2个集合合并,并且去掉重复的
交集:寻找2个集合或多个集合之间相同的成员
差集:所有属于A且不属于B的元素构成的集合,叫做集合A减集合B(或集合A与集合B之差)
(1)并集
let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let union = new Set([...s1, ...s2]);
console.log(union);
(2)交集
let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let intersect = new Set([...s1].filter((ele) => {
return s2.has(ele);
}));
console.log(intersect);//Set(1) {3}
(3)差集
let s1 = new Set([1, 2, 3]);
let s2 = new Set([3, 4, 5]);
let difference = new Set([...s1].filter((ele) => {
return !s2.has(ele);
}));
console.log(difference)//Set(2) {1, 2}
三.WeakSet
WeakSet结构与Set类似,也是不重复的值的集合
WeakSet的成员只能是对象(任何具有Iterable接口的对象),不能是其他类型的值
(原生js对象不具备这个条件)
const a=[1,2];
new WeakSet(a);// Uncaught TypeError: Invalid value used in weak set
const ws = new WeakSet({ a: 1, b: 2 });
console.log(ws);//object is not iterable (cannot read property Symbol(Symbol.iterator))
上述代码会报错,因为WeakSet只能存储对象或者类似数组的对象
1.WeakSet特点
(1) 成员都是数组或类似数组的对象,若调用add()
方法时,传入了非数组和类似数组的对象的参数,就会抛出错误
(2) 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM
节点,不容易造成内存泄露.
(3) WeakSet
不可迭代,因此不能被用在for...of
等循环中.
(4) WeakSet
没有size
属性
2.WeakSet成员弱引用
WeakSet中的对象都是弱引用,也就是说:垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存.
弱引用:弱引用就是不保证不被垃圾回收机制回收的对象,它拥有比较短暂的生命周期,在垃圾回收机制运行时,只要发现具有弱引用的对象,就会回收它的内存
3.不会引发内存泄漏
因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存.结束使用该值之后,有时忘记取消引用,导致内存无法释放,故而引起内存泄露,WeakSet里面的引用,都不计入垃圾回收机制,所以不存在这个问题,
因此,WeakSEt适合临时存放一组对象,以及存放跟对象绑定的信息,只要对象在外部消息,它在WeakSet里面的引用就会自动消失.
4.使用WeakSet
const ws = new WeakSet();
WeakSet 可以接受一个数组或类似数组的对象作为参数(实际上,任何具有Iterable接口的对象,都可以作为WeakSet的参数)
const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
console.log(ws);//WeakSet {Array(2), Array(2)}
注意:a
数组的成员成为 WeakSet 的成员,而不是a
数组本身。这意味着,数组的成员只能是对象。也就是说,如果我们要传数组的话必须使用[[1,2]]
数组将里层对象或数组给包起来.
5.WeakSet的方法
- WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
- WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
- WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在
const ws = new WeakSet();
const obj = {};
const foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false
ws.delete(window);
ws.has(window); // false
四.Map
Map是一个构造函数,用来生成Map数据结构,它类似于对象,也是键值对的集合,但是键可以是非字符串
Map
中存储的是key-value
形式的键值对,其中key和value
可以是任何数据类型的,即对象也可以作为key
,Map
的出现,就是让各种类型的值都可以当做键,Map
提供的是值-值的对应.也就是后端语言常用的数据字典
1.Map和原生Object的区别
(1) Object
对象有原型,也就是说它有默认的key
值在对象上面,除非我们使用object.create(null)
创建一个没有原型的对象
(2) Object
对象中,只能把string和symbol
作为key
值,但是在Map
中,key
值可以是任何数据类型的(String,Number,Boolean,Undefined,NaN,Null)或对象(Map,Set,Object,Function,Symbol)
(3) 通过Map
中的size
属性,可以很方便的获取Map
的长度,要获取Object
长度,只能手动计算
传统 Object 用字符串作键,Object 结构提供了“字符串 — 值”的对应Map 结构提供了“值 — 值”的对应,是一种更完善的 Hash 结构实现如果你需要“键值对”的数据结构,Map 比 Object 更快速 更高效 更合适。
2.Map的使用
初始化 Map 需要一个二维数组,或者直接初始化一个空的 Map
const m = new Map();
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m);//Map(0) {}
console.log(m1);//Map(2) {"name" => "liuqiao", "age" => 27}
3.属性方法
1. size
返回成员总数
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.size);//2
2.set(key,value)
设置键值对
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.set("sex","男");
console.log(m1);//Map(3) {"name" => "liuqiao", "age" => 27, "sex" => "男"}
3.get(key)
获取键对应的值
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.get("name"));//liuqiao
4.has(key)
是否存在某个键
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.has("name"));//true
5.delete(key)
删除某个键值对,返回一个布尔值,表示是否删除成功
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.delete("age"));//true
console.log(m1);// {"name" => "liuqiao"}
6.clear()
将Map中的所有元素全部删除
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.clear();
console.log(m1);//Map(0) {}
4.遍历Map
1.forEach遍历
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.forEach((val, key, map) => {
console.log("val", val);
console.log("key", key);
console.log("map", map);
});
2.for…of遍历
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (const val of m1) {
console.log(val);//打印的是数组
}
//可以使用如下方式,得到键和值
for (const [key,value] of m1) {
console.log(key+" "+val);
}
3.keys()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let key of m1.keys()) {
console.log(key)
}
//name,age
4.values()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let value of m1.values()) {
console.log(value)
}
//liuqiao,27
5.entries()
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (const val of m1.entries()) {
console.log(val);//打印的是数组
}
//可以使用如下方式,得到键和值
for (const [key,value] of m1.entries()) {
console.log(key+" "+val);
}
实际上,你会发现,for...of...遍历map等同于使用map.entries()
5.数据类型转换
1.Map转为数组
const map=new Map();
const arr=[...map];
2.数组转为Map
let arr=[];
let map=new Map(arr);
3.Map转为对象
const obj={};
for (let [k, v] of map) {
obj[k] = v
}
4.对象转为Map
let map=new Map();
for( let k of Object.keys(obj)){
map.set(k,obj[k])
}
6.重复键的情况
1.引用类型情况
const m1 = new Map();
m1.set(['a'], 5);
m1.set(['a'], 6);
console.log(m1);//Map(2) {Array(1) => 5, Array(1) => 6}
console.log(m1.get(['a']));//undefined
只有对同一个对象的引用,Map 结构才将其视为同一个键
Map的键本质上还是跟内存地址绑定的,只要内存地址不一样,就会看成2个不同的键
2.值类型情况
const m1 = new Map();
m1.set('name','zhangsan');
m1.set('name','lisi');
console.log(m1);//Map(1) {"name" => "lisi"}
值类型就不会有引用地址的问题,如果键名有重复的,则以后面的准,会把已经存在的替换掉
五.WeakMap
WeakMap结构与Map结构类似,也是用于生成键值对的集合
1.特点
- 只接受对象作为键名(
null
除外),不接受其他类型的值作为键名 - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有
get
、set
、has
、delete
2.设计目的
WeakMap设计目的在于,有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用
const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
[e1, 'foo 元素'],
[e2, 'bar 元素'],
];
e1和e2是两个对象,我们通过arr数组对这两个对象添加文字说明,这就形成了arr对e1和e2的引用
WeakMap
就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
注意:WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
3.WeakMap使用
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
4应用场景
在网页的 DOM 元素上添加数据,就可以使用WeakMap
结构。当该 DOM 元素被清除,其所对应的WeakMap
记录就会自动被移除
<template>
<div id="app">
<div class="info-item" v-for="[label, value] in infoMap" :key="value">
<span>{{label}}</span>
<span>{{value}}</span>
</div>
</div>
</template>
data: () => ({
info: {},
infoMap: {}
}),
mounted () {
this.info = {
name: 'jack',
sex: '男',
age: '28',
phone: '13888888888',
address: '广东省广州市',
duty: '总经理'
}
const mapKeys = ['姓名', '性别', '年龄', '电话', '家庭地址', '身份']
const result = new Map()
let i = 0
for (const key in this.info) {
result.set(mapKeys[i], this.info[key])
i++
}
this.infoMap = result
}
六.总结
1.Set
- 是一种叫做集合的数据结构(ES6新增的)
- 成员唯一、无序且不重复
-
[value, value]
,键值与键名是一致的(或者说只有键值,没有键名) - 允许储存任何类型的唯一值,无论是原始值或者是对象引用
- 可以遍历,方法有:
add
、delete
、has
、clear
2.WeakSet
- 成员都是对象
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存
DOM
节点,不容易造成内存泄漏 - 不能遍历,方法有
add
、delete
、has
3.Map
- 是一种类似于字典的数据结构,本质上是键值对的集合
- 可以遍历,可以跟各种数据格式转换
- 操作方法有:
set
、get
、has
、delete
、clear
4.WeakMap
- 只接受对象作为键名(
null
除外),不接受其他类型的值作为键名 - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有
get
、set
、has
、delete
本文地址:https://blog.csdn.net/liuqiao0327/article/details/107901064
上一篇: 鼠标放上去超链接的文字逐个加色效果