本文做为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的使用几率基本很低,这里就纯作一个整理了,往后万一用到,或者说使用逐渐普及,也方便查找。
那么就写到这里了。