Set的使用

Set

基本API

使用 new 关键字可以创建一个空集合,同时也可以在创建的时候进行初始化

  //创建一个空集合
    const m1 = new Set()

    //创建的同时初始化实例

    //使用数组初始化集合
    const s1 = new Set(["value1","value2","value3"])

    console.log(s1.size)        //3

    //使用自定义迭代器初始化集合
    const s2 = new Set({
        [Symbol.iterator]:function*(){
            yield "value1";
            yield "value2";
            yield "value3";
        }
    })
    console.log(s2.size)        //3
  • 初始化之后可以使用add()增加值,使用has()查询,通过size()取得元素数量,以及delete()和clear()删除元素

与 Map 类似, Set 可以包含任何JS数据类型作为值

const m = new Set()

    const functionKey = function () {}
    const symbolKey = Symbol()
    const objectKey = new Object();

    m.add(functionVal)
    m.add(symbolVal)
    m.add(objectVal)

    console.log(m.has(functionVal))         //true
    console.log(m.has(symbolVal))           //true
    console.log(m.has(objectVal))           //true

顺序与迭代

Set 会维护值插入时的顺序,因此支持按顺序迭代
集合实例可以提供一个迭代器(Iterator),能以插入顺序生成集合内容.可以通过value()方法及其别名方法keys()(或者Symbol.iterator属性,它引用value())取得这个迭代器:

const s = new Set(["value1","value2","value3"])
  console.log(s.values === s[Symbol.iterator])    //true
  console.log(s.keys === s[Symbol.iterator])      //true
  
    for(let values of s.values()){
        alert(values)    //value1,value2,value3
    }

因为values()是默认迭代器,所以可以直接对集合实例使用扩展操作,把集合转换为数组:

 const s = new Set(["val1","val2","vla3","val4"])
    console.log([...s])

集合entries()方法返回一个迭代器,可以按照插入顺序产生两个包含元素的数组,这两个元素是集合中每个值的重复出现:

const s = new Set(["vla1", "vla2", "vla3", "vla4", "vla5"]);
    for(let m of s.entries()){
        console.log(m)
    }

    //["val1","val1"] ["val2","val2"] ["val3","val3"]

如果不使用迭代器,而是使用回调方法,则可以调用集合的forEach()方法并传入回调,依次迭代每个键/值对.传入的回调接收可选的第二个参数,这个参数用于重写回调内部this的值:

const s = new Set(["value1", "value2", "value3"]);

    s.forEach((val,dupVal) => alert(`${val} -> ${dupVal}`))

修改属性的值不会影响其作为集合值的身份:

 const s1 = new Set(["value"])

    //字符串原始值作为值不会被改变
    for(let value of s1.values()){
        value = "newValue"
        console.log(value)      //newValue
        console.log(s1.has("value"))        //true
    }

    const valObj = {id:1}
    const s2 = new Set([valObj])

      //修改值对象的属性,但对象仍然存在于集合中
    for(let value of s2.values()){
        value.id = "newValue"
        console.log(value)      //{id: 'newValue'}
        console.log(s2.has(valObj))     //true
    }
    console.log(valObj)     //{id: 'newValue'}
  • 字符串:在 Set 中,字符串是不可变的,所以修改循环中的变量值不会影响 Set 中的元素。
  • 对象:在 Set 中,对象是通过引用存储的,所以修改循环中的变量(实际上是对象的引用)会影响 Set 中的实际对象。

定义正式集合操作

Set 和 Map 相似: 很多开发者喜欢使用Set, 但需要手动实现;或者类似子类化Set,或者定义一个实用函数库.要把两种方式合二为一,可以在子类上实现静态方法,然后在实例方法中使用这些静态方法.

  • 某些Set操作是有关联性的,因此最好让实现的方法能支持处理多个集合实例
  • Set 保留插入顺序,所有返回的集合必须保证顺序
  • 尽可能高效地实用内存,扩展操作符的语法很简洁,但尽可能避免集合和数组间的相互转换能够节省对象初始化成本
  • 不要修改已有的集合实例.union(a,b)或a.union(b)应该返回包含结果的新集合实例.
class XSet extends Set {
        union(...sets) {
            return XSet.union(this, ...sets);
        }

        intersection(...sets) {
            return XSet.intersection(this, ...sets);
        }

        difference(...sets) {
            return XSet.difference(this, ...sets);
        }

        symmetricDifference(set) {
            return XSet.symmetricDifference(this, set);
        }

        cartesianProduct(sets) {
            return XSet.cartesianProduct(this, sets);
        }

        powerSet() {
            return XSet.powerSet(this);
        }

        // 并集
        static union(a, ...bSets) {
            const unionSet = new XSet(a);
            for (const b of bSets) {
                for (const bValue of bSets) {
                    unionSet.add(bValue);
                }
            }
            return unionSet;
        }

        // 交集
        static intersection(a, ...bSets) {
            const intersectionSet = new XSet(a);
            for (const aValue of intersectionSet) {
                for (const b of bSets) {
                    if (!b.has(aValue)) {
                        intersectionSet.delete(aValue);
                    }
                }
            }
            return intersectionSet
        }

        // 差集
        static difference(a, b) {
            const differenceSet = new XSet(a);
            for (const bValue of b) {
                if (a.has(bValue)) {
                    differenceSet.delete(bValue);

                }
            }
            return differenceSet;
        }

        // 对称差集
        static symmetricDifference(a, b) {
            return a.union(b).difference(a.intersection(b))
        }

        // 笛卡尔积
        static cartesianProduct(a, b) {
            const cartesianProductSet = new XSet();
            for (const aValue of a) {
                for (const bValue of b) {
                    cartesianProductSet.add([aValue, bValue]);
                }
            }
        }

        // 幂集
        static powerSet(a) {
            const powerSet = new XSet([new XSet()]);
            for (const aValue of a) {
                for (const set of new XSet(powerSet)) {
                    powerSet.add(new XSet(set).add(aValue))
                }
            }
            return powerSet;
        }
    }

WeakSet

基本API

可以使用 new 关键字来实例化一个空的 WeakSet

const m = new WeakSet()

弱集合中的值只能是Object或者继承自Object的类型

  • 可以使用数组初始化弱集合
  • 初始化之后可以使用add()添加新值,has()查询,还可以使用delete()删除
  • add()方法返回弱集合实例,所以可以把多个操作连缀起来

弱值

WeakSet 表示弱集合的值,这些值不属于正式的引用,不会阻止垃圾回收

add()方法初始化了一个新对象,并将他用作一个值.因为没有指向这个对象的其他引用,所以当这行代码执行完毕之后,这个对象的值就会被当作垃圾回收.然后这个值就从弱集合中消失了,使其成为一个空集合:

  const m = new WeakSet()
  m.add({})

container对象维护着一个对弱集合值的引用,因此这个对象不会成为垃圾回收的目标,不过,如果调用了rem(),就会摧毁值对象的最后一个引用,垃圾回收程序就可以把这个值清理掉

  const m = new WeakSet()
  const container = {
    val:{}
  }
  m.add(container.val)

  function rem(){
    container.val = null
  }

不可迭代值

因为 WeakSet 中的值任何时候都可能被销毁,所以没必要提供迭代其值的能力
WeakSet 之所以限制只能用对象作为值,是为了保证只有通过值对象的引用才能取得值.

使用弱集合

相比于WeakSet 实例,WeakSet 实例的用处没有那么大.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值