ES6-15【map与set】

一、ES6新增数据结构(set/map)

(1)set/map概念

两个都是es6新增的构造函数:

  1. set:类似于数组,只能存值没有键,值是唯一的
  2. map:类似于对象,可以存键和值,键名不限于字符串,键和值一一对应

set、map、Promise、Proxy四个无法通过babel编译语法降级(poly feel、babel poly feel)

(2)set/map深入

  1. set

    1. 原型

      var set = new Set();
      console.log(set);
      // Set(0) {size: 0}
      // [[Entries]]
      // No properties
      // size: 0
      // [[Prototype]]: Set
      // add: ƒ add()
      // clear: ƒ clear()
      // constructor: ƒ Set()
      // delete: ƒ delete()
      // entries: ƒ entries()
      // forEach: ƒ forEach()
      // has: ƒ has()
      // keys: ƒ values()
      // size: (...)
      // values: ƒ values()
      // Symbol(Symbol.iterator): ƒ values()
      // Symbol(Symbol.toStringTag): "Set"
      // get size: ƒ size()
      // [[Prototype]]: Object
      
    2. 参数

      参数是具备 iterator 接口的数据结构:[],类数组

      var set = new Set([5,7]); 
      console.log(set);
      // Set(2) {5, 7}
      // [[Entries]]
      // 0: 5
      // 1: 7
      // size: 2
      // [[Prototype]]: Set
      
    3. 声明方式

      //方法一
      var set = new Set([1,2,3]);
      console.log(set); //Set(3) {1,2,3}
      //方法二
      var set = new Set();
      set.add(1).add(2).add(3);
      console.log(set); //Set(3) {1,2,3}
      
    4. 值是唯一的

      var set = new Set([1,2,3,4,5,5,5,5]);
      console.log(set); //Set(5) {1,2,3,4,5}
      
    5. 特殊值

      var set = new Set([undefined,undefined,null,null,5,'5',true,1,NaN,NaN,{},{},[],[]])
      console.log(set);
      // Set(11) {undefined, null, 5, '5', true, …}
      // [[Entries]]
      // 0: undefined
      // 1: null
      // 2: 5
      // 3: "5"
      // 4: true
      // 5: 1
      // 6: NaN     注意:1. 在set里面NaN是等于NaN的
      // 7: Object        2. {},[]是构造器实例出来的唯一引用,所以不等
      // 8: Object
      // 9: Array(0)
      // 10: Array(0)
      // size: 11
      // [[Prototype]]: Set
      
    6. 操作方法

      1. add
        追加数据,返回值是set实例

        var set = new Set();
        var x = {id:1};
        var y = {id:2};
        //一般用法
        set.add(x);
        set.add(y);
        //链式调用
        set.add(x) // 返回值是set实例本身
           .add(y)
           .add(x); // 引用相同,无法添加
        console.log(set); 
        // Set(2) {{…}, {…}}
        // [[Entries]]
        // 0:
        // value: {id: 1}
        // 1:
        // value: {id: 2}
        // size: 2
        // [[Prototype]]: Set
        
      2. size
        返回当前长度

        console.log(set.size); // 2
        
      3. delete
        清除某个值,返回值是布尔值,操作是实时的

        console.log(set.delete(y));  // false
        console.log(set);
        // Set(2) {{…}, {…}}
        // [[Entries]]
        // 0:
        // value: {id: 1}
        // [[Prototype]]: Set
        
      4. clear
        清空所有的值,返回值是undefined,操作是实时的

        // 注意打印位置: 发现输出结果一致,说明操作是实时的
        console.log(set);
        // Set(0) {size: 0}
        // [[Entries]]
        // No properties
        // size: 0
        // [[Prototype]]: Set
        set.clear()
        console.log(set);
        // Set(0) {size: 0}
        // [[Entries]]
        // No properties
        // size: 0
        // [[Prototype]]: Set
        
      5. has
        判断是否有指定值,返回值是布尔值

        console.log(set.has(x)); // true
        
    7. 遍历方法

      1. keys/values/entries

        遍历键,值,键值对数组,返回值是迭代器对象

        set 不存在键名,故键名和键值是一致的

        let set = new Set([1, 2, 3, 4, 5, 6, 7]);
        console.log(set.keys()); // SetIterator {1, 2, 3, 4, 5, …}
        console.log(set.values()); // SetIterator {1, 2, 3, 4, 5, …}
        console.log(set.entries()); // SetIterator {1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, …}
        
        for (let i of set.keys()) {
            console.log(i); // 1 2 3 4 5 6 7
        }
        for (let i of set.values()) {
            console.log(i); // 1 2 3 4 5 6 7
        }
        for (let i of set.entries()) {
            console.log(i); // [1,1] [2,2] [3,3] [4,4] [5,5] [6,6] [7,7]
        }
        
      2. 迭代器
        set对象上也部署了迭代器,可通过for…of遍历

        // 判断遍历的是键还是值,结果是一致的,说明实际上是在调用set实例上的values方法
        console.log(Set.prototype[Symbol.iterator] === Set.prototype.values);//true
        for(let values of set){
            console.log(values); //1 2 3 4 5 6 7
        }
        
      3. forEach
        遍历成员(键、值、set结构)

        let set = new Set(['a', 'b', 'c', 'd']);
        set.forEach((value, keys, s) => {
            console.log(value, keys, s)
        })
        // a a Set(4) {'a', 'b', 'c', 'd'}
        // b b Set(4) {'a', 'b', 'c', 'd'}
        // c c Set(4) {'a', 'b', 'c', 'd'}
        // d d Set(4) {'a', 'b', 'c', 'd'}
        
    8. 使用场景

      1. 拓展运算符-set 结构转化为数组,去重

        去重最简单的方式就是收集到set当中,再展开到一个数组里

        // ...能展开具备迭代器接口的数据结构
        let set = new Set(['a','b','c','d','e','f','g']); 
        console.log(...set); //a b c d e f g
        
        let set = new Set(['a','b','c','d','e','f','g'];
        console.log([...set]); //['a','b','c','d','e','f','g']
        
      2. map-操作成员

        let set = new Set([1,2,3,4,5,6,7])
        let set1 = new Set([...set].map(value => value*2)); 
        console.log(set1); // Set(7) {2, 4, 6, 8, 10, …}
        

        难点

        var arr = [1,2,3,4]
        var arr1 = arr.map(parseInt);
        console.log(arr1);//[1,NaN,NaN,NaN]
        //原理就是map里的参数 value, index,默认传给了parseInt
        var arr1 = arr.map((value,idx)=> console.log(value,idx));
        //1 0
        //2 1
        //3 2
        //4 3
        //那么就代表着,第二个参数转换进制,无法转换就是NaN
        parseInt(1,0)parseInt(2,1)parseInt(3,2)parseInt(4,3);
        	
        let set2 = new Set([[...set].map(parseInt)]);  //这里是数组里嵌套数组,所以size是1
        console.log(set2);
        // Set(1) {Array(7)}           
        // [[Entries]]
        // 0: Array(7)
        // value: Array(7)
        // 0: 1
        // 1: NaN
        // 2: NaN
        // 3: NaN
        // 4: NaN
        // 5: NaN
        // 6: NaN
        // length: 7
        // [[Prototype]]: Array(0)
        // size: 1
        // [[Prototype]]: Set
        
      3. filter-过滤成员

        let set2 = new Set([...set].filter(x => (x%2) == 0))
        console.log(set2); //{2,4,6}
        
    9. 交集并集差集

      let a = new Set([1, 2, 3]);
      let b = new Set([4, 3, 2]);
      //并集
      let union = new Set([...a, ...b]);
      console.log(union); //Set(4) {1, 2, 3, 4}
      //交集
      let intersect = new Set([...a].filter(x => b.has(x)));
      console.log(intersect); //Set(2) {2, 3}
      //差集
      let difference = new Set([...a].filter(x => !b.has(x)));
      console.log(difference) //Set(1) {1} 
      
    10. 映射新的结构

      //方法一
      let set = new Set([1,2,3]);
      let set1 = new Set([...set].map(value => value*2)); 
      //写法二
      let set = new Set([1,2,3]);
      let set1 = new Set(Array.from(set,value=>value*2));
      
  2. map

    1. 原型

      和set的原型基本一致,只是多了set、get方法,主要用于存取键值,而set本身没有键,所以不需要这两个方法

    2. 与对象对比

      1. 普通对象

        键和值不能实现一一对应

        var m = {}
        var x = {id:1},
        	y = {id:2};
        m[x] = "foo"; // => m['object Object'] = "foo"; 
        m[y] = "bar"; // => m['object Object'] = "bar";
        console.log(m); //{[object Object]:"bar"}
        console.log(m[x]); //bar
        console.log(m[y]); //bar    
        //当键名是对象的时候会调用toString从而发生覆盖
        
      2. map结构

        键和值一一对应

        let m = new Map();
        var x = {id:1},
            y = {id:2};
        m.set(x,'foo'); // Tips: 1.这里是通过set设置的
        m.set(y,'bar');
        console.log(m.get(x));//foo 2. 这里通过get访问的
        console.log(m.get(y));//bar
        
    3. 参数

      1. 参数是具备 iterator 接口的数据结构:[],类数组
      2. 参数是以键值对存在的二维数组 [['键名', '键值'], ...]
      let m = new Map([
      	['name', 'zhangsan'],
      	['title', 'lisi']
      ])
      console.log(m)
      // Map(2) {'name' => 'zhangsan', 'title' => 'lisi'}
      // [[Entries]]
      // 0: {"name" => "zhangsan"}
      // 1: {"title" => "lisi"}
      // size: 2
      // [[Prototype]]: Map
      
    4. 声明方式

      // 方法一
      let m = new Map([
      	['name', 'wangwu'],
      	['title', 'zhaoliu']
      ])
      //传参实现原理:
      var items = [['name','wagwu'],['title','zhaoliu']]
      let m = new Map();
      items.forEach(([key,value]) => m.set(key,value))
      
      // 方法二
      let m = new Map();
      map.set('name','zhangsan')
      map.set('title','lisi')
      
    5. 覆盖问题

      1. 原始值

        const map  = new Map();
        // 地址相同所以会覆盖
        map.set(1, 'foo'); // Map(1) {1 => 'foo'}
        map.set(1, 'bar'); // Map(1) {1 => 'bar'}
        // 认定-0和+0是全等的,只是Object.is判断是false
        map.set(-0,123);
        console.log(map.get(+0)) //123
        // 不会有隐式转换
        map.set(true,1);
        map.set('true',2);
        console.log(true); // 1
        // 键名也可以是undefined和unll
        map.set(undefined,1)
        map.set(null,2)
        log(map.get(undefined)); //1
        log(map.get(null)); //2
        // 认定NaN是等于NaN的,只是Object.is判断是true
        map.set(NaN,123);
        log(map.get(NaN));//123
        
      2. 引用值

        // 指针不同所以访问不到
        map.set([5],555);
        console.log(map.get([5])); // undefined
        // 指针相同所以可以访问
        var arr = [5];
        map.set(arr,555); 
        console.log(map.get(arr)); //555 
        

        原始值在栈里面存储的是地址,引用值在栈里面存储的是指向堆的指针

    6. 操作方法

      1. set
        添加成员,返回值set实例本身
        const m  = new Map();
        // 链式调用
        m.set(1,"foo").set(2,"bar");  
        console.log(m); // Map(2) {1 => 'foo', 2 => 'bar'}
        
      2. get
        获取成员,返回值set实例本身
        m.get(1); //'foo'
        
      3. size
        获取成员长度
        var x = {id:1},
            y = {id:2};
        m.set(x,"foo");
        m.set(y,"bar") ;  
        cosnole.log(m.size); //2 
        
      4. delete
        删除成员,返回值是布尔值,操作是实时的
        var x = {id:1},
            y = {id:2};
        m.set(x,"foo");
        m.set(y,"bar") ;  
        cosnole.log(m.delete(x)); //true
        m.delete(x); // Map(1) {{...} => "bar"}
        
      5. clear
        清空成员,返回值undefined,操作是实时的
        var x = {id:1},
            y = {id:2};
        m.set(x,"foo");
        m.set(y,"bar") ;  
        cosnole.log(m.clear()); //undefined
        m.clear(); // Map(0) {}
        
      6. has
        判断成员,返回值是布尔值
        var x = {id:1},
            y = {id:2};
        m.set(x,"foo");
        m.set(y,"bar") ;  
        cosnole.log(m.has(x)); //true
        
    7. 遍历方法

      1. keys/values/entries
        var x = {id:1},
            y = {id:2}
        m.set(x,"foo")
        m.set(y,"bar") 
         
        for(let keys of m.keys()){
            console.log(keys)
        }
        // {id:1}
        // {id:2}
         
        for(let keys of m.values()){
            console.log(values)
        }
        // foo
        // bar
         
        for(let keys of m.entries()){
            console.log(entries)
        }
        // [{...},'foo']
        // [{...},'bar']
        
      2. 迭代器
        console.log(m[Symbol.iterator] === m.entries) //true 说明实际在调用map实例上的entries方法
        for(let i of m){
            console.log(i)
        }
        // [{...},"foo"]
        // [{...},"bar"]
        
        // 模式匹配
        for(let [key,values] of m){
            console.log(key,values)
        }
        // {id:1}"foo"
        // {id:2}"bar"
        
    8. 使用场景

      1. 拓展运算符-map 结构转化为数组

        const myMap = new Map();
        myMap.set(true,7)
        	 .set({foo:3},['abc']);
        console.log(myMap); //Map(2) {true=>7, {...}=>Array(1)}
        console.log([...myMap]); 
        // (2) [Array(2), Array(2)]
        // 0: (2) [true, 7]
        // 1: (2) [{...}, Array(1)]
        // length: 2
        // __proto__: Array(0)
        
      2. 拓展运算符-数组转化为map 结构

        const map = new Map([
            [true,7],
            [{foo:3},['abc']]
        ]);
        console.log(map); // Map(2) {true => 7, {…} => Array(1)}
        
      3. 拓展运算符-map 结构转化为对象(条件:键名为字符串)

        const myMap = new Map();
        myMap.set({},7)
            .set({},'abc');
        function strMapToObj(strMap){
            let obj = Object.create(null);
            for(let [key,val] of strMap.entries()){
                obj[key] = val;
            }
            return obj;
        }   
        console.log(strMapToObj(myMap)); //{true: 7, a: 'abc'}
        
      4. 拓展运算符-对象转化为map 结构

        function objToStrMap(obj){
            let map = new Map();
            for(let key of Object.keys(obj)){
                map.set(key,obj[key]);
            }
            return map
        }
        console.log(objToStrMap({true:7,no:false})); //Map(2) {'true' => 7, 'a' => 'abc'}
        

        对象没有部署迭代器接口,为什么这里还能使用for…of呢?
        这是因为Object.keys把它变成了键名构成的数组

(3)set/map对比array/object

  1. set对比array

    let set = new Set();
    let arr = new Array;
     
    //增加
    set.add({t:1}); //Set(1) {{t:1}}
    arr.push({t:1}); //[{t:1}]
     
    //查询
    set.has({t:1}); //false 因为不是同一个指针
    arr.find(item => item.t); //{t: 1}
     
    //修改
    set.forEach(item => item.t ? item.t = 2:""); //Set(1) {{t:2}}
    arr.forEach(item => item.t ? item.t = 2:""); //[{t:2}]
     
    //删除
    set.forEach(item => item.t ? set.delete(item):''); //Set(0) {size: 0}
    let index = arr.findIndex(item => item.t);
    arr.splice(index,1); //[]
    
  2. map对比array

    let map = new Map();
    let arr = new Array();
    //增加
    map.set('t',1); //Map(1) {'t' => 1}
    arr.push({'t':1}); //[{t,1}]
     
    //查询
    map.has('t'); //true
    arr.find(item => item.t); //{t: 1}
     
    //修改
    map.set('t',2); //Map(1) {'t' => 2}
    arr.forEach(item => item.t ? item.t = 2 : ''); //[{t,2}]
     
    //删除
    map.delete('t'); //Map(0) {size: 0}
    let index = arr.findIndex(item => item.t);
    arr.splice(index,1); //[]
    
  3. map/set对比object

    let map = new Map();
    let set = new Set();
    let item = {t:1}
    let obj= {} 
    //增加
    map.set('t',1); //Map(1) {'t' => 1}
    set.add(item); //Set(1) {{t:1}}
    obj['t'] = 1; //{t:1}
     
    //查询
    map.has('t'); //true
    set.has(item); //true
    't' in obj; //true
    obj.hasOwnProperty('t') //true 
     
    //修改
    map.set('t',2); //Map(1) {'t' => 2}
    item.t = 2; //Set(1) {{t:2}}
    obj['t'] = 2; //{t:2}
     
    //删除
    map.delete('t'); //Map(0) {size: 0}
    set.delete(item); //Set(0) {size: 0}
    delete obj['t']; //{}
    

(4)结论

map/set都有迭代器接口,底层优化比array/object做得更好,结构操作比array/object更优雅,若对数据结构要求比较高,且保证数据唯一性的话就用map/set而不用array/object

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值