前言

当我们需要存储一些数据的时候,首先想到的是定义一个变量用来存储,之后我们可能学了数组,发现数组比变量可以存储更多的数据,接着可能有其它的存储数据的方法等等,然而我今天需要介绍的是在ES6中比较常见使用的数据类型结构,Set和Map。

Set数据结构

一、Set简介

  • set中成员的值都是唯一的,没有重复的值
  • 向set中添加成员时,不会发生类型转换
  • 向set中添加的对象总是不想等

    二、常用的属性和方法

    属性:

  • size:返回set实例的成员总数

    方法:

  • add():添加某个值,返回set本身

  • delete():删除某个值,返回一个布尔值,判断删除是否成功
  • has():返回一个布尔值,表示该值是否为set成员
  • clear():清除所有成员,没有返回值
  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员

    三、实例剖析

    为了方便大家更好的了解和学习set,我在这里会将这些方法和属性进行分类整理和讲解
    1、set的基本用法

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>数组去重</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const set=new Set(); //创建set数据结构
    10. [1,1,2,3,4,5].forEach(x=>{
    11. set.add(x);
    12. })
    13. console.log(set); //1,2,3,4,5
    14. for(let i of set){
    15. console.log(i);
    16. }
    17. </script>
    18. </body>
    19. </html>

    map和set - 图1
    可以看到向Set添加成员时,不会添加重复的值
    2、数组作为参数传入到set结构中

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title></title>
    6. </head>
    7. <body>
    8. <div></div>
    9. <div></div>
    10. <div></div>
    11. <div></div>
    12. <div></div>
    13. <script type="text/javascript">
    14. // const set=new Set([1,2,3]);
    15. // console.log(...set);//1,2,3,使用扩展运算符
    16. const set=new Set(document.querySelectorAll('div'));
    17. console.log(set.size);//5 size返回set实例的成员总数
    18. //如上代码相当于
    19. const item=new Set();
    20. document.querySelectorAll('div').forEach(x=>{
    21. item.add(x);
    22. })
    23. console.log(item);
    24. console.log(item.size);//5
    25. </script>
    26. </body>
    27. </html>

    map和set - 图2
    3、set中添加的值不会发生类型转换

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>向set中添加成员时,不会发生类型转换</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let mySet=new Set();
    10. mySet.add(5);
    11. mySet.add('5');
    12. console.log(mySet);//5,'5'
    13. let set=new Set();
    14. let a=NaN;
    15. let b=NaN;
    16. set.add(a);
    17. set.add(b);
    18. console.log(set);//NaN
    19. </script>
    20. </body>
    21. </html>

    向 Set 加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。
    4、set中添加的对象总是不想等的

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>向set中添加的对象总是不想等</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let set=new Set();
    10. set.add({});//向set中添加一个空对象
    11. console.log(set.size);//1
    12. set.add({});//向set中添加另一个空对象
    13. console.log(set.size);//2
    14. </script>
    15. </body>
    16. </html>

    由于两个空对象不相等,所以它们被视为两个值
    5、Array.from()方法
    Array.from()可以将set结构转为数组,这就提供了去除数组重复成员的另一种方法

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Array.from()方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const items=new Set([1,2,3,4,5]);
    10. console.log(items);//1,2,3,4,5
    11. const array=Array.from(items);
    12. console.log(array);//1.2.3,4,5
    13. //Array.from()方法实现数组去重
    14. function dedupe(arr){
    15. return Array.from(new Set(arr))
    16. }
    17. console.log(dedupe([1,1,2,3]));//1,2,3
    18. </script>
    19. </body>
    20. </html>

    四.遍历的应用

    1、数组去重

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>数组去重</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let set=new Set(['red','blue','green']);
    10. let arr=[...set];
    11. console.log(arr);//red,blue,green
    12. //数组去重
    13. let arrs=[1,1,3,3,4,5,6];
    14. let unique=[...new Set(arrs)];
    15. console.log(unique);//1,3,4,5,6
    16. </script>
    17. </body>
    18. </html>

    2、间接使用数组的方法

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>间接使用数组的方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let set=new Set([1,2,3,4,5]);
    10. set=new Set([...set].map(x=>x*2));//使用数组的map方法
    11. console.log(set);//2,4,6,8,10
    12. let mySet=new Set([1,2,3,4,5]);
    13. mySet=new Set([...mySet].filter(x=>x%2==0));//使用数组的filter方法
    14. console.log(mySet);//2,4
    15. </script>
    16. </body>
    17. </html>

    map和set - 图3
    3、实现并集,交集,差集

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>实现并集,交集,差集</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let a=new Set([1,2,3]);
    10. let b=new Set([4,3,2]);
    11. //并集
    12. let union=new Set([...a, ...b]);
    13. console.log(union);//1,2,3,4
    14. //交集
    15. let intersect=new Set([...a].filter(x=>b.has(x)));
    16. console.log(intersect);//2,3
    17. //差集
    18. let difference=new Set([...a].filter(x=>!b.has(x)));
    19. console.log(difference);//1
    20. </script>
    21. </body>
    22. </html>

    4、同步改变原来的 Set 结构
    如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>同步改变原来的 Set 结构</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. //方法一
    10. let set=new Set([1,2,3]);
    11. set=new Set([...set].map(val=>val*2));
    12. console.log(set);//2,4,6
    13. //方法二
    14. let mySet=new Set([1,2,3]);
    15. mySet=new Set(Array.from(mySet,val=>val*2));
    16. console.log(mySet);//2,4,6
    17. </script>
    18. </body>
    19. </html>

    五、set实例属性和方法

    1、size属性

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>set中的size属性</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const set=new Set();
    10. //向set中添加成员
    11. set.add(1);
    12. set.add(2);
    13. set.add(3);
    14. //链式方法
    15. set.add(4).add(5).add(6);
    16. console.log(set.size);//6
    17. </script>
    18. </body>
    19. </html>

    2、操作方法add()、delete()、has()、clear()

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>set中的操作方法add()、delete()、has()、clear()</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const set=new Set();
    10. //向set中添加成员
    11. set.add(1);
    12. set.add(2);
    13. set.add(3);
    14. //链式方法
    15. set.add(4).add(5).add(6);
    16. console.log(set.size);//6
    17. console.log(set.has(1));//true
    18. set.delete(1);
    19. console.log(set.has(1));//false
    20. set.clear();//清空全部set成员
    21. console.log(set.size);//0
    22. </script>
    23. </body>
    24. </html>

    3、遍历方法keys()、values()、entries()、forEach()

    注意:set的遍历顺序就是插入顺序,由于set没有键名,只有键值(或者说键名和键值是同一个值),所以keys()和values()方法的行为完全一致

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>set中的遍历方法keys(),values(),entries(),forEach()</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. let set=new Set(['red','blue','green']);
    10. //遍历全部的key
    11. for(let key of set.keys()){
    12. console.log(key);//red,blue,green
    13. }
    14. //遍历全部的value
    15. for(let value of set.values()){
    16. console.log(value);//red,blue,green
    17. }
    18. //遍历全部的key和value
    19. for(let item of set.entries()){
    20. console.log(item);//['red','red'],['blue','blue'],['green','green']
    21. }
    22. set.forEach((key,value)=>{
    23. console.log(key+':'+value);
    24. })
    25. </script>
    26. </body>
    27. </html>

    map和set - 图4

    Map数据结构

    一、Map简介

    es6中的map很大程度上和set相似,但是map是以键值对的形式存储数据的

    二、常用的属性和方法

    属性:

  • size:返回map结构的成员总数

    方法:

  • set(key,value):设置键名key对应的键值value,然后返回整个map结构,如果key已经有值,则键值会被更新,否则就新生成该键

  • get(key):读取key对应的键值,如果找不到key,则返回undefined
  • has(key):返回一个布尔值,表示某个键是否在当前map对象中
  • delete(key):删除某个key,返回true,如果删除失败,返回false
  • clear():清除所有成员,没有返回值
  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():遍历map的所有成员

    三、实例剖析

    1、size属性
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>map中的size属性</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const map=new Map();
    10. map.set('foo',true);
    11. map.set('bar',false);
    12. console.log(map.size);//2
    13. </script>
    14. </body>
    15. </html>
    2、set(key,value)方法
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>map中的set()方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const map=new Map();
    10. map.set('1','a');//键是字符串
    11. map.set(2,'b');//键是数值
    12. map.set(undefined,'3');//键是undefined
    13. console.log(map);//'1'=>a,2=>b,undefinde=3
    14. const myMap=new Map();
    15. //链式写法
    16. myMap.set(1,'a').set(2,'b').set(3,'c');
    17. console.log(myMap);//1=>a,2=>b,3=>c
    18. </script>
    19. </body>
    20. </html>
    3、get(key)方法
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>map中的get()方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const map=new Map();
    10. const hello=function(){
    11. console.log('你好');
    12. }
    13. map.set(hello,'es6');//键是函数
    14. console.log(map.get(hello));//es6
    15. </script>
    16. </body>
    17. </html>
    4、has(key)方法
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>map中的has()方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const map=new Map();
    10. //链式写法
    11. map.set('a',1).set('b',2).set('c',3);//向map中添加成员
    12. console.log(map.has('a'));//true
    13. console.log(map.has('b'));//true
    14. console.log(map.has('c'));//true
    15. console.log(map.has('d'));//false
    16. </script>
    17. </body>
    18. </html>
    5、delete(key)方法
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>map中的delete方法</title>
    6. </head>
    7. <body>
    8. <script type="text/javascript">
    9. const map=new Map();
    10. map.set('a',1);
    11. console.log(map.has('a'));//true
    12. map.delete('a');//删除键a
    13. console.log(map.has('a'));//false
    14. </script>
    15. </body>
    16. </html>
    6、clear()方法
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>map中的clear()方法</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const map=new Map();
  10. map.set('foo',true);//向map中添加成员
  11. map.set('bar',false);
  12. console.log(map.size);//2
  13. map.clear();//清除map中的全部成员
  14. console.log(map.size);//0
  15. </script>
  16. </body>
  17. </html>

7、遍历方法keys()、values()、entries()、forEach()

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>遍历方法keys(),values(),entries(),forEach()</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const map=new Map();
  10. //向map中添加成员
  11. map.set(1,'a');
  12. map.set(2,'b');
  13. map.set(3,'c');
  14. //遍历全部的键
  15. for(let key of map.keys()){
  16. console.log(key);//1,2,3
  17. }
  18. //遍历全部的值
  19. for(let values of map.values()){
  20. console.log(values);//a,b,c
  21. }
  22. //遍历全部的键和值
  23. for(let [key,value] of map.entries()){
  24. console.log(key,value);//1=>a,2=>b,3=>c
  25. }
  26. for(let item of map.entries()){
  27. console.log(item[0],item[1]);//1=>a,2=>b,3=>c
  28. }
  29. for(let [key,value] of map){
  30. console.log(key,value);//1=>a,2=>b,3=>c
  31. }
  32. map.forEach(function(value,key){
  33. console.log(key,value);
  34. })
  35. </script>
  36. </body>
  37. </html>

map和set - 图5
注意:
这里的forEach()是值在前,键在后
8、map结构转为数组

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>map结构转为数组</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const map=new Map([
  10. [1,'one'],
  11. [2,'two'],
  12. [3,'three']
  13. ]);
  14. console.log([...map.keys()]);//[1,2,3]
  15. console.log([...map.values()]);//one,two,three
  16. console.log([...map]);//[1,one],[2,two],[3,three]
  17. console.log([...map.entries()]);//[1,one],[2,two],[3,three]
  18. const map1=new Map(
  19. [...map].filter(([key,value])=>key<3)//使用数组的filter方法
  20. );
  21. console.log(map1);//1=>one,2=>two
  22. </script>
  23. </body>
  24. </html>

三、Map与其它数据结构的转换

作为键值对存储数据的map与其它数据也可以进行转换,我们看下下面的案例
1、Map转为数组
map转为数组最方便的方法,就是使用扩展运算符…

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Map转为数组</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const map=new Map();//创建map数据结构
  10. map.set(true,7);//向map中添加成员
  11. map.set({foo:3},['abc']);
  12. console.log([...map]);//[[true,7],[{foo:3},['abc']]];
  13. </script>
  14. </body>
  15. </html>

2、数组转为Map
将数组传入map构造函数,就可以转为map

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>数组转为map</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const map=new Map([
  10. [true,7],
  11. [{foo:3},['abc']]
  12. ]);
  13. console.log(map);
  14. //map{
  15. //true=>7,
  16. //object:{foo:3}=>['abc'];
  17. //}
  18. </script>
  19. </body>
  20. </html>

3、Map转为对象
如果所有map的键都是字符串,它可以无损的转为对象,如果有非字符串的键名,那么这个键名会被转为字符串,再作为对象的键名

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>map转为对象</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. function strMapObj(strMap){
  10. let obj=Object.create(null);//创建一个新对象
  11. for(let [key,value] of strMap){//遍历循环strMap
  12. obj[key]=value;
  13. }
  14. return obj;
  15. }
  16. const map=new Map().set('yes',true).set('no',false);
  17. console.log(strMapObj(map));//{yes:true,no:false}
  18. </script>
  19. </body>
  20. </html>

在这里了我需要讲解一下Object.create()这个方法,官方的意思是:Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>测试Object.create()方法</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. const person={
  10. isHuman: false,
  11. printIntroduction: function () {
  12. console.log(this.name,this.isHuman);
  13. }
  14. }
  15. const me = Object.create(person);
  16. me.name = "Matthew";
  17. me.isHuman = true;
  18. me.printIntroduction();//Mattew,true
  19. </script>
  20. </body>
  21. </html>

map和set - 图6
4、对象转为Map

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>对象转为map</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. function objToMap(obj){
  10. let map=new Map();
  11. for(let key of Object.keys(obj)){
  12. map.set(key,obj[key]);
  13. }
  14. return map;
  15. }
  16. console.log(objToMap({yes:true,no:false}));//yes=>true,no=>false
  17. </script>
  18. </body>
  19. </html>

在这里讲解Object.keys()方法,官方解释:**Object.keys()** 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用,for..in循环遍历该对象时返回的顺序一致 。如果对象的键-值都不可枚举,那么将返回由键组成的数组。
参数:
要返回其枚举自身属性的对象
返回值:
一个表示给定对象的所有可枚举属性的字符串数组

  1. // simple array
  2. var arr = ['a', 'b', 'c'];
  3. console.log(Object.keys(arr)); // console: ['0', '1', '2']
  4. // array like object
  5. var obj = { 0: 'a', 1: 'b', 2: 'c' };
  6. console.log(Object.keys(obj)); // console: ['0', '1', '2']
  7. // array like object with random key ordering
  8. var anObj = { 100: 'a', 2: 'b', 7: 'c' };
  9. console.log(Object.keys(anObj)); // console: ['2', '7', '100']
  10. // getFoo is a property which isn't enumerable
  11. var myObj = Object.create({}, {
  12. getFoo: {
  13. value: function () { return this.foo; }
  14. }
  15. });
  16. myObj.foo = 1;
  17. console.log(Object.keys(myObj)); // console: ['foo']

5、Map转为JSON字符串
map转为json要区分两种情况,一种情况时map的键名都是字符串,这时可以选择转为对象json,另一种情况是map的键名有非字符串,这时可以选择转为数组json。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>map转为json</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. function strMapToObj(strMap){
  10. let obj=Object.create(null);//创建一个新对象
  11. for(let [key,value] of strMap){
  12. obj[key]=value;
  13. }
  14. return obj;
  15. }
  16. function strMapToJson(strMap){
  17. return JSON.stringify(strMapToObj(strMap));
  18. }
  19. let map=new Map().set('yes',true).set('no',false);
  20. let obj=strMapToJson(map);
  21. console.log(obj);//{'yes':true,'no':false}
  22. function mapToArrayJson(map){
  23. return JSON.stringify([...map]);
  24. }
  25. let fooMap=new Map().set(true,7).set({foo:3},['abc']);
  26. let foo=mapToArrayJson(fooMap);
  27. console.log(foo);//[[true:7],[{foo:3},['abc']]]
  28. </script>
  29. </body>
  30. </html>

6、JSON字符串转为Map
json转为map,正常情况下所有键名都是字符串,但是有一种特殊情况,整个json就是一个数组,且每个数组成员本身,又是一个有两个成员的数组,这时它可以一一对应的转为map,这往往是map转为数组json的逆操作。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>json转为map</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. function objTostrMap(obj){
  10. let map=new Map();
  11. for(let key of Object.keys(obj)){
  12. map.set(key,obj[key]);
  13. }
  14. return map;
  15. }
  16. function jsonToStrMap(jsonStr){
  17. return objTostrMap(JSON.parse(jsonStr));
  18. }
  19. let obj1=jsonToStrMap('{"yes": true, "no": false}')
  20. console.log(obj1);//yes=>true,no=>false
  21. function jsonToMap(jsonStr){
  22. return new Map(JSON.parse(jsonStr));
  23. }
  24. let obj2=jsonToMap('[[true,7],[{"foo":3},["abc"]]]')//true=>7,{foo:3}=>['abc']
  25. console.log(obj2);
  26. </script>
  27. </body>
  28. </html>

本文参考:
http://es6.ruanyifeng.com/#docs/set-map

总结

本章我们主要学习了ES6中set和map相关的属性和方法,set和map的方法中有很多都是相同的方法,has()、delete()、clear()、keys()、values()、entries、forEach()等等。