es6入门7--Set Map数据结构

本文做为ES6入门第十三章的学习整理笔记,可能会包含少部分我的的理解推测,若想阅读更详细的介绍,还请阅读原文ES6入门es6

1、set数据结构数组

1.set不接受重复值数据结构

ES6新增了Set构造函数用于建立set数据结构,这种结构相似于数组,但有很大的一个区别就是,set数据结构不接受重复值,每一个值都是惟一的。dom

咱们能够经过Set构造函数快速建立一个set数据结构,顺便打印看看究竟长什么样:函数

let s = new Set();
console.dir(s);

那么能够看到,set实例具备一个size属性,由于咱们还未给此结构添加值,因此是0,相似于数组的length属性。学习

set实例还有不少方法,例如add添加,clear清除,还有在数组拓展中已经介绍过的keys,values等比较熟悉的方法,这些后面具体再说。spa

咱们尝试在new命令时直接初始化值:prototype

let s = new Set([1,2,1,3]);

能够看到,尽管我添加了两个数字1,最终的set实例结构中只有一个不重复的1,这是由于set不接受重复的值,自带去重效果。code

你可能看过如下数组去重的快捷方法,正式利用的set的这一特色:对象

// 数组去重
[...new Set([1, 1, 2, 3, 4, 4])];
Array.from(new Set([1, 1, 2, 3, 4, 4]));

2.set实例的增删改查方法

add方法:添加某个值,返回添加值后的set解构,相似数组的push,后添加的元素在set解构后面。

let s = new Set();
s.add(1).add(2);

has方法:查找set解构是否包含某值,返回一个布尔值。

s.has(1); //true
s.has(3); //false

delete方法:删除某个值,返回一个布尔值对应是否删除成功。

s.delete(1);//true
s.delete(1);//false

clear方法:清除整个set解构,无返回值。

s.clear();

 3.set的遍历方法

keys方法:遍历元素的键名

values方法:遍历元素的键值

entries方法:遍历元素的键值对

forEach方法:用的贼多,回调函数遍历每一个元素

在数组拓展这一章节中也有介绍这三个方法,这里就简单说下;三个方法都是结合for...of循环使用,分别遍历元素的key,value与key/value组合

let s = new Set([{a:1}, {b:2}, {c:3}]);
for (let item of s.keys()) {
    console.log(item);// {a:1}, {b:2}, {c:3}
};
for (let item of s.values()) {
    console.log(item);// {a:1}, {b:2}, {c:3}
};
for (let item of s.entries()) {
    console.log(item);// [{a:1},{a:1}],[{b:2},{b:2}],[{c:3},{c:3}]
};

经过上述代码中的输出能够了解到,keys方法与values方法执行彻底相同,这是由于set解构没有key名致使,key名与value相同;而entries方法每次返回的是一个包含了key与value的数组。

当咱们想遍历出set解构的每一个元素理论上使用values方法,有趣的是set解构的默认遍历器恰好与values相等,因此咱们甚至能省略掉values方法直接遍历解构中的每一个元素。

let s = new Set([1, 2, 3]);
Set.prototype[Symbol.iterator] === Set.prototype.values; //true
//省略values方法
for(let item of s){
    console.log(item);//1 2 3
};

 与数组中使用这三个方法的区别在于,数组中的keys遍历的是元素的下标,values相同,entries是下标和元素组成键值对,且不是数组。

当咱们使用forEach遍历set结构数据时,回调参数三个参数的前两个彻底相同,这也是由于key名与key值相同的缘故,这点须要注意。

let s = new Set([1, 2, 3]);
s.forEach((val,key) => console.log(val,key))//1 1,2 2,3 3

4.set解构的做用

a.数组去重,主要利用了set不接受重复值作参数的特色。

b.set结构实现并集,简单点说,就是把两个set重复项去掉,原理仍是利用set不接受重复项

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

c.set结构实现交集,原理是利用了set实例的has方法

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

d.set结构实现差集,同理利用了has方法

let s3 = new Set([...a].filter(x => !b.has(x)))//set {1}

你的直觉是否是这里应该是{1,4},这里的差集实际上是a里面有且b里面没有的元素,而不是ab互相没有。

2、WeakSet结构

WeakSet数据结构与Set相似,也不接受重复的值,但也有三点不一样,一是WeakSet解构的成员只能是对象,二是WeakSet中的对象都是弱引用,三是WeakSet没法遍历

1.WeakSet成员只能是对象

let s = new WeakSet();

s.add([{a:1},{b:2}]);
console.dir(s);

s.add(1);//报错 Invalid value used in weak set

建立WeakSet 结构可经过new命令完成,WeakSet 接受任何含有Iterable接口的对象做为参数。能够看到当咱们add非对象元素,该操做报错,可是add添加对象没问题。

那么咱们看这段代码,为何报错了:

let s = new WeakSet([1,2,3]);

我在前面你说了,WeakSet的每一个成员必须是对象,前面咱们使用的是add方法,每次添加都是一个成员,这是直接使用new初始化,虽然传递的参数是数组,但本质上等同于:

let s = new WeakSet();
s.add(1).add(2).add(3);

因此咱们须要保证数组中的每一个元素也是对象,这样就不会报错了:

let s = new WeakSet([{a:1},{b:2}]);

其次能够看到WeakSet方法并很少,add,has,delete三个,用法和set相同,这里就不重复介绍了。

2.WeakSet结构成员均为弱引用

咱们都知道,当一个对象不被任何地方引用,垃圾回收机制就会释放掉这个对象所占用内存。咱们在前面说WeakSet的成员都是对象,可是垃圾回收机制不考虑WeakSet的引用。

说直白点,如今对象a被A和WeakSet同时引用,A再也不引用了垃圾回收机制就直接释放了,彻底无论WeakSet还在引用它。

也正是由于WeakSet成员是弱引用的缘由,咱们没法保证何时成员就被释放了,因此WeakSet没有size属性,也不可遍历。

3、map数据结构

1.基本用法与增删改查方法

传统意义上的对象都是键值对组成的集合,键为字符串,值为一个对象,咱们是没法使用对象做为键的。

但Map打破了这个规则,咱们能够经过Map建立键值都是对象的数据结构,这样键再也不是做为保存值的存在,在遍历时,键值均可以是有效的对象。

let m = new Map();
console.dir(m);

从上图中,能够看到百分之80的方法与Set数据结构彻底相同,只是多了一个set方法和get方法。

set(key,value)方法:按照key/value添加成员,返回Map结构,支持链式写法;若是key已存在,则覆盖

get(key)方法:按照key查找返回对应的value,若是未找到,返回undefined。

has(key)方法:查找是否包含某个key,返回一个布尔值。

delete(key)方法:删除对应的key,返回一个布尔值,表示是否成功删除。

clear()方法:清空整个Map数据结构。

let m = new Map();
let o = {name:'echo'};
m.set(o,{age:26});
m.get(o);//{age:26}
m.has(o);//true
m.delete(o);//true
m.has(o);//false

那么在上述代码中,咱们为map数据结构添加了一个key为{name:'echo'}值为{age:26}的成员。

同时咱们能够经过get指定的key访问到对应的value,delete仍是同样返回是否删除成功,has依旧是判断该数据结构是否含有此成员。

添加成员固然不要求经过set,在new命令执行时,咱们能够以一个数组的形式传递须要添加的成员。

let m = new Map([
    ['name', '听风是风'],
    ['age', 26]
]);
m.has('name') //true
m.get('name') //听风是风
m.has('age') //true
m.get('age') //26

其实初次看到这我是有点懵逼的,为何我一个数组成员的两个元素,成了Map数据结构中一个成员的key与value。其实这个不难理解,它等同于如下的执行:

let arr = [
    ['name', '听风是风'],
    ['age', 26]
];
let m = new Map();
arr.forEach(([key, value]) => m.set(key, value))

数组每一个元素又是一个双元素数组,前者做为map的key,后者做为map的value

须要注意的是,map数据结构一样不接受重复的值做为成员,这里的重复是指key名相同,若是相同,后者会覆盖前者

const m = new Map([
    ['name', 1],
    ['name', 2]
]);
console.log(m);//key:name value:2

除此以外,当咱们map的key是对象时,须要注意对象引用的问题:

let o = {name:1};
let m = new Map();
m.set(o,2)
console.log(m.get(o));//2
m.set({name:1},2)
console.log(m.get({name:1}));//undefined

在上述代码中,若是咱们直接将{name:1}做为key用于存值,在set执行时,没法拿到对应的value,这是由于对象尽管写法相同,但仍然是彻底不一样的两个东西;

因此在须要将对象作key时,请将此对象赋予一个变量,利用此变量做为key进行存储,在读取时再次读取这个变量,就能够避免这个问题了。

其实说到这里,关于map的key,实际上是跟内存地址相关。若是key是一个简单数据类型,那么只要两个key彻底相等,就视为一个key,且后者覆盖前者,若是不相等,则反之。

若是key是一个对象,想正确的存取,请将对象赋予给一个变量再作set操做。不然会由于引用地址问题没法访问到你已经添加的key。

2.Map数据结构的遍历方法

keys()方法:遍历并返回键名

let m = new Map([
    ['name', '听风是风'],
    ['age', 26]
]);
for(let key of m.keys()){
    console.log(key);//name age
};

values()方法:遍历并返回键值

for(let value of m.values()){
    console.log(value);//听风是风 26
};

entries()方法:遍历返回全部成员,注意,我没说这里是返回键值对

for(let item of m.entries()){
    console.log(item);
};

如上图,返回两个数组,每一个数组分别包含了key和value,因此若是咱们想直接访问key,value,应该这么写:

for(let [key,value] of m.entries()){
    console.log(key,value);//name 听风是风,age 26
};

还记得在介绍Set数据结构是,咱们说Set的默认遍历器接口等于values方法,因此咱们能够简写遍历,比较好运的是,Map数据结构的默认遍历器接口等于entries方法,因此咱们还能够继续简写:

m[Symbol.iterator] === m.entries; //true
for (let [key, value] of m) {
    console.log(key, value);//name 听风是风,age 26
};

forEach方法,经过回调参数也能够方便的访问到Map结构的key与value

m.forEach((value, key, m) => console.log(value, key));//听风是风 name,26 "age"

4、WeakMap数据结构

 WeakMap与Map结构相似,但也有两点不一样,一是WeakMap成员的key只接受对象

let m = new WeakMap();
m.set('name',1);//报错

二是WeakMap的键名所引用对象为弱引用,也就是不计入垃圾回收机制,这点与WeakSet一致。

let m = new WeakMap();
let ele = document.querySelector("#div");
m.set(ele, '这是一个div元素');
m.get(ele); //这是一个div元素

在上述代码中,咱们先是将获取的dom存在了ele变量中,此时对于div dom的引用次数是1次。

而后咱们又将ele做为key,为这个ele添加了一些说明,照理说,div dom此时又被WeakMap结构引用,因此div引用次数是2次。

但因为WeakMap的key名对象是弱引用,因此这里div一共的引用此事仍是1次。当咱们让ele再也不引用div元素时,垃圾回收机制不会考虑WeakMap对于div的引用,而是直接释放,这点其实与WeakSet是保持一致的。

强调一点的是,WeakMap弱引用的是key,而不是value,这里有个例子:

let m = new WeakMap();
let key = {};
let value = {a: 1};
m.set(key, value);
value = null;
m.get(key) //{a:1}

即使咱们将WeakMap中value所引用的对象释放,其实垃圾回收机制仍是将WeakMap的引用计为1次,因此还能正常读取到。

由于key是弱引用的缘故,因此与WeakSet同样,不存在遍历方法。

WeakMap结构最大的一个用处就是用于保存dom,这样dom元素被删除也不会形成内存泄漏问题:

let ele = document.getElementById('logo');
let fn = function () {
    console.log(1)
};
let m = new WeakMap();
//将dom元素与须要执行的函数做为WeakMap结构的key与value
m.set(ele, fn);
//为dom元素增长监听
ele.addEventListener('click', function () {
    //执行监听函数
    m.get(ele)();
}, false);

关于WeakMap这里就很少作介绍了,至少我目前开发基本使用不到.....

不仅是是WeakMap,Set与Map的使用几率基本很低,这里就纯作一个整理了,往后万一用到,或者说使用逐渐普及,也方便查找。

那么就写到这里了。

相关文章
相关标签/搜索