ES6 提供了两种新的数据结构 Set和Map.Set是集合,Map是字典
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)
在Set中成员的值是唯一的,重复的值自动被过滤掉
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3);//{1, 2, 3, 4}
返回成员总数
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.size);//去重之后,成员总数为4
添加某个值,返回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}
删除某个值,返回一个bool值,表示是否删除成功
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.delete(4));//true
返回一个bool值,表示该值是否为Set的成员
let s3 = new Set([1, 2, 2, 3, 3, 4])
console.log(s3.has(5));//false
清除所有成员,没有返回值
let s3 = new Set([1, 2, 2, 3, 3, 4])
s3.clear();
console.log(s3);//Set(0) {}
得益于数据结构Set查找更快速更高效,但是也因为数据结构的内部数据是无序的,无法实现按下标查询,修改,排序等操作
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
console.log(s1[0]);//undefined
console.log(s1['a']);//undefined
Set没有类似getter方法,取值怎么取呢?
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"]
使用回调函数遍历每个成员
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遍历时,第一个参数和第二个参数的值永远都是一样
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1) {
console.log(val);
}
可以循环遍历出Set中的每一个值
返回键名的遍历器
由于 Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys
方法和 values
方法的行为完全一致。
var array = [1, 2, 'a', 3, 'b'];
let s1 = new Set(array);
for (const val of s1.keys()) {
console.log(val);
}
返回键值的遍历器。
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
返回键值对的遍历器
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"]
Array
的indexOf
方法比Set
的has
方法效率低下Set
不含有重复值(可以利用这个特性对数组进行去重)Set
通过delete
方法删除某个值,而Array
只能通过splice
,两者的使用方便程度,前者更优.Array
的很多新方法map
,filter
,some
,ervery
等是Set
没有的(但是两者可以互相转换来使用)使用Array.from方法可以将Set结构转为数组
const items = new Set([1, 2, 3, 4, 5])
const array = Array.from(items)
// 方法一:
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]
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}
(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结构与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) 成员都是数组或类似数组的对象,若调用add()
方法时,传入了非数组和类似数组的对象的参数,就会抛出错误
(2) 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM
节点,不容易造成内存泄露.
(3) WeakSet
不可迭代,因此不能被用在for...of
等循环中.
(4) WeakSet
没有size
属性
WeakSet中的对象都是弱引用,也就是说:垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存.
弱引用:弱引用就是不保证不被垃圾回收机制回收的对象,它拥有比较短暂的生命周期,在垃圾回收机制运行时,只要发现具有弱引用的对象,就会回收它的内存
因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存.结束使用该值之后,有时忘记取消引用,导致内存无法释放,故而引起内存泄露,WeakSet里面的引用,都不计入垃圾回收机制,所以不存在这个问题,
因此,WeakSEt适合临时存放一组对象,以及存放跟对象绑定的信息,只要对象在外部消息,它在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]]
数组将里层对象或数组给包起来.
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
中存储的是key-value
形式的键值对,其中key和value
可以是任何数据类型的,即对象也可以作为key
,Map
的出现,就是让各种类型的值都可以当做键,Map
提供的是值-值的对应.也就是后端语言常用的数据字典
(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 更快速 更高效 更合适。
初始化 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}
返回成员总数
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.size);//2
设置键值对
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.set("sex","男");
console.log(m1);//Map(3) {"name" => "liuqiao", "age" => 27, "sex" => "男"}
获取键对应的值
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.get("name"));//liuqiao
是否存在某个键
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.has("name"));//true
删除某个键值对,返回一个布尔值,表示是否删除成功
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
console.log(m1.delete("age"));//true
console.log(m1);// {"name" => "liuqiao"}
将Map中的所有元素全部删除
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
m1.clear();
console.log(m1);//Map(0) {}
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);
});
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);
}
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let key of m1.keys()) {
console.log(key)
}
//name,age
const m1 = new Map([["name", "liuqiao"], ["age", 27]]);
for (let value of m1.values()) {
console.log(value)
}
//liuqiao,27
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()
const map=new Map();
const arr=[...map];
let arr=[];
let map=new Map(arr);
const obj={};
for (let [k, v] of map) {
obj[k] = v
}
let map=new Map();
for( let k of Object.keys(obj)){
map.set(k,obj[k])
}
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个不同的键
const m1 = new Map();
m1.set('name','zhangsan');
m1.set('name','lisi');
console.log(m1);//Map(1) {"name" => "lisi"}
值类型就不会有引用地址的问题,如果键名有重复的,则以后面的准,会把已经存在的替换掉
WeakMap结构与Map结构类似,也是用于生成键值对的集合
null
除外),不接受其他类型的值作为键名get
、set
、has
、delete
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}
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
在网页的 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
}
[value, value]
,键值与键名是一致的(或者说只有键值,没有键名)add
、delete
、has
、clear
DOM
节点,不容易造成内存泄漏add
、delete
、has
set
、get
、has
、delete
、clear
null
除外),不接受其他类型的值作为键名get
、set
、has
、delete