• Set集合



    ES6学习系列 😃

    长久以来,数组一直是JavaScript中唯一的集合类型,不过,有一些开发者认为非数组对象也是集合,只不过是键值对集合,它们的用途与数组完全相同。在ES6之前,由于可选的集合类型有限,数组使用的又是数值型索引,因而经常被用于创建队列和栈。如果开发者们需要使用非数值型索引,就会用非数组对象创建所需的数据解构,而这是Set集合与Map集合的早期实现。
    Set集合是一种无重复元素的列表,开发者们一般不会像访问数组元素那样逐一访问每个元素,通常的做法是检测给定的值在某个集合中是否存在。Map集合内含多组键值对,集合中每个元素分别存放着可访问的键名和它对应的值,Map集合经常被用于缓存频繁取用的数据。在ES6标准正式发布以前,开发者们已经在ES5中用非数组对象实现了类似的功能。

    ES5中的Set集合和Map集合

    用对象模拟集合

    var set = Object.create(null);
    set.foo = true;
    
    // 检查属性是否存在
    if(set.foo){
        // 要执行的代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    变量set是一个原型为null的对象,不继承任何属性,在ES5中,开发者们经常用类似的方法检测对象的某个属性是否存在,在这个示例中,将set.foo赋值为true,通过条件语句可以确认该值存在于当前对象中。
    模拟Map集合唯一的区别在于存储的值不同。

    var map = Object.create(null);
    map.foo = "bar";
    
    // 获取值
    var value = map.foo;
    // bar
    console.log(value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    该解决方案的一些问题

    如果程序比较简单,确实可以用对象来模拟Set集合和Map集合,但如果触碰到对象属性的某些限制,那么这个方法就会变得更加复杂。比如所有对象的属性名必须是字符串类型 ,必须确保每个键名都是字符串类型且在对象中是唯一的。

    var map = Object.create(null);
    map[5] = "foo";
    // foo
    console.log(map["5"]);
    // foo
    console.log(map[5]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    本来属性名是数值型的 5,但是会自动转换为字符串,最后map[“5”]和map[5]引用的是同一个属性。因此如果想分别用数字和字符串作为对象属性的键名,则内部的自动转换机制会导致很多问题。
    同样的,用对象最为属性的键名也会遇到类似的问题。

    var map = Object.create(null);
    var key1 = {},key2 = {};
    map[key1] = "foo2";
    // foo2
    console.log(map[key2]);
    // foo2
    console.log(map["[object Object]"]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对象也会自动转换为字符串作为键名。用不同对象作为对象属性的键名理论上应该指向多个属性,但实际上都是同一个属性"[object Object]"。由于对象会被转换为默认的字符串表达方式,因此其很难用作对象舒心的键名。
    对于Map集合来说,如果他的属性值是假值,则在要求使用布尔值的情况下会被自动转为false。强制转换本身没有问题,但是某些场景下,就会导致错误发生。

    var map = Object.create(null);
    map.count = 0;
    // 本来是检查count属性是否存在,实际上检查的是改值是否非零
    if(map.count){
        // 要执行的代码 这里并不会执行
        console.log(map.count);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个示例中,本来是检查map中是否包含count,但是count为0时,if语句中的代码却不会被执行。因为这里会被自动转换为false。
    在大型软件中,一旦发生此类问题将难以定位及调试,从而ES6中需要加入Set集合和Map集合这两种新特性。

    ES6中的Set集合
    • 创建Set集合并添加元素

    Set集合不会对值进行强制的类型转换,数字5和字符串"5"可以作为两个独立元素存在,引擎内部通过Object.is方法检测两个值是否一致,唯一的例外是,Set集合中的+0和-0被认为时相等的。

    // 调用new Set创建Set集合
    let set = new Set();
    // 通过add方法添加元素
    set.add(5);
    set.add("5");
    /*
        * 通过size属性获取集合中目前的元素数量,Set集合不会对值进行强制的类型转换
        * 数字5和字符串"5"可以作为两个独立元素存在
        * 引擎内部通过Object.is方法检测两个值是否一致,唯一的例外是,Set集合中的+0和-0被认为时相等的
        */
    // 2
    console.log(set.size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    同样,向Set集合中添加两个对象,它们之间彼此保持独立。

    let set = new Set(), key1 = {}, key2 = {};
    set.add(key1);
    set.add(key2);
    // 2
    console.log(set.size);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果多次调用add方法并传入相同的值作为参数,那么后续的调用实际上会被忽略

    let set = new Set();
    set.add(5);
    set.add("5");
    // 重复 - 本次调用直接被忽略
    set.add(5);
    // 2
    console.log(set.size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由于第二次传入的数字5是一个重复值,因此不会被添加到集合中。
    也可以用数组来初始化Set集合,Set构造函数同样会过滤掉重复的值从而保证集合中的元素各自唯一。

    let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
    // 5
    console.log(set.size);
    
    • 1
    • 2
    • 3

    自动去重的功能对于将已有代码或者JSON结构转换为Set集合执行得非常好。

    实际上,Set构造函数可以接受所有可迭代对象作为参数,数组、Set集合、Map集合都是可迭代的,因而都可以作为Set构建函数的参数使用;构造函数通过迭代器从参数中提取值。

    • 检测元素是否存在

    可以通过has()方法检测Set集合中是否存在某个值

    let set = new Set();
    set.add(5);
    set.add("5");
    // true
    console.log(set.has(5));
    // false
    console.log(set.has(6));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 移出元素

    通过delete()方法可以移出Set集合中的某一个元素,调用clear()方法会移除集合中的所有元素。

    let set = new Set();
    set.add(5);
    set.add("5");
    // true
    console.log(set.has(5));
    set.delete(5);
    // false
    console.log(set.has(5));
    // 1
    console.log(set.size);
    set.clear();
    // false
    console.log(set.has("5"));
    // 0
    console.log(set.size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • forEach遍历元素

    forEach方法中的回调参数接受以下三个参数

    1. Set集合中下一次索引的位置
    2. 与第一个参数一样的值
    3. 被遍历的Set集合本身
      其实这和数组和Map是一致的。数组和Map集合的forEach方法的回调参数都接受3个参数,前两个分别是值和键名(对于数组来说就是数值型索引值)。
    let set = new Set([1,2]);
    set.forEach(function(value,key,ownerSet){
        console.log(key + " " + value);
        console.log(ownerSet === set);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行结果如下

    1 1
    true
    2 2
    true
    
    • 1
    • 2
    • 3
    • 4

    在Set集合的forEach()方法中,第二个参数也与数组一样,如果需要在回调函数中使用this引用,则可以将它作为第二个参数传入forEach函数。

    let set = new Set([1,2]);
    let processor = {
        output(value){
            console.log(value);
        },
        process(dataSet){
            dataSet.forEach(function(value){
                this.output(value);
            },this);
        }
    }
    processor.process(set);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这个示例中,processor.process方法调用了Set集合的forEach方法并将this传入作为回调函数的this值,从而this.output()方法可以正确地调用processor.output()方法。forEach()方法的回调函数之使用了第一个参数value,所以直接省略了其他参数,在这里也可以使用箭头函数,这样无需将this作为第二个参数传入回调函数了。

    let set = new Set([1,2]);
    let processor = {
        output(value){
            console.log(value);
        },
        process(dataSet){
            dataSet.forEach(value => this.output(value));
        }
    }
    processor.process(set);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在此示例中,箭头函数从外围的process函数读取this值,所以可以正确的将this.output方法解析为一次processor.output调用。

    • 转换数组

    尽管Set集合更适合用来跟踪多个值,而且又可以通过forEach方法操作集合中的每一个元素,但是你不能像访问数组元素那样直接通过索引访问集合中的元素,如有需要,最好先将Set集合转换为一个数组。

    let set = new Set([1,2,3,3,3,4,5]),array = [...set];
    // [1, 2, 3, 4, 5]
    console.log(array);
    
    • 1
    • 2
    • 3

    删除数组中的重复元素,可以将数组转为集合然后再转为数组即可、

    function eliminateDuplicates(items) {
        return [...new Set(items)];
    }
    let nums = [1, 2, 3, 3, 3, 4, 5];
    let noDuplicates = eliminateDuplicates(nums);
    // [1, 2, 3, 4, 5]
    console.log(noDuplicates);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    35.滚动 scroll
    交易履约之结算平台实践 | 京东云技术团队
    Vue3实战笔记(49)—Vue 3响应式魔法:ref vs reactive深入对决
    科技云报道:产业为根大模型应用为擎,容联云推动企业营销服场景重塑
    Vue基础学习笔记02
    Dapr在Java中的实践 之 服务调用
    Photoshop使用笔记总目录
    了解服务级别协议(SLA)在 ITSM 中的重要性
    Linux:CentOS7 开启路由转发
    PHP cURL 函数
  • 原文地址:https://blog.csdn.net/m0_37607945/article/details/127836273