JavaScript进阶(2)——js集合与es6的Set类、js字典与es6的Map类

本文探讨JavaScript中的集合和字典概念,通过实例介绍如何使用对象模拟实现集合操作,并讨论ES6引入的Set和Map类,包括它们的特性、交集、并集、差集及子集操作。同时,文章还涵盖了WeakMap和WeakSet的使用场景和优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本博客学习书籍:《学习JavaScript数据结构与算法》
本博客学习章节:第六章,第七章

集合

集合是由一组无序且不能重复的项组成的。

为了更好的贴合这个定义,我们用JavaScript的对象来实现集合。JavaScript的对象不允许一个键指向两个不同的属性,保证了集合里的元素都是唯一的。

首先用函数法来模拟实现Set类。

function Set(obj) {
    let items = obj || {};
    
    // 集合里的元素是不能重复的,所以在add、remove的时候需要判断元素是否已存在
    this.has = function(value){
        return value in items;
        // 或
        // return items.hasOwnProperty(value);
    }

    // 添加
    this.add = function (value) {
        if(!this.has(value)){
           items[value] = value;
           return true;
        }else{
           return console.error('不能加入重复的项')
        }
    }

    // 删除
    this.remove = function (value) {
        if(this.has(value)){
           delete items[value];
           return true;
        }else{
           return console.error('该项不存在')
        }
    }

    // 清空
    this.clear = function(){ items = {};  };

    // 长度
    this.size = function(){ return Object.keys(items).length; }

    // values
    this.values = function () {
        // let values = [];
        // for (let i = 0, keys = Object.keys(items); i < keys.length; i++) {
        //     values.push(items[keys[i]]);
        // }
        // return values;

        return Object.values(items)
    };
  
    return items;
}

集合操作

我们对集合可以进行如下操作:

  • 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
  • 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
  • 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。
  • 子集:验证一个给定集合是不是另一集合的子集。

并集

并集就是把两个Set()合并成一个新Set()。我们Set类的元素是用对象元素实现的,所以,新Set()也是用对象元素实现的。

function Set(obj) {
    let items = obj || {};
    
    // Set类的基本实现方法 ...
    // 并集
	this.union = function (otherSet) {
        let unionSet = new Set();

        let values = this.values();
        for (let i = 0; i < values.length; i++) {
            unionSet.add(values[i]);
        }

        values = otherSet.values();
        for (let i = 0; i < values.length; i++) {
            unionSet.add(values[i]);
        }
        return unionSet;
    };
    
    return items;
}

交集

交集,查找第一个Set()里有没有第二个Set()里相同的元素。如果有的话,就push交集set()里。

function Set(obj) {
    let items = obj || {};
    
    // Set类的基本实现方法 ...
    // 交集
	this.intersection = function(otherSet){
        let intersectionSet = new Set();

        let values = this.values();
        values.forEach((v)=>{
            if(otherSet.has(v)){
                intersectionSet.add(v);
            }
        });
        return intersectionSet;
    }
    
    return items;
}

差集

差集,查找第一个Set()里有的而第二个Set()里没有的元素,过滤出来就push差集set()里。

function Set(obj) {
    let items = obj || {};
    
    // Set类的基本实现方法 ...
    // 差集
	this.difference = function(otherSet){
        let differenceSet = new Set();

        let values = this.values();
        values.forEach((v)=>{
            if(!otherSet.has(v)){
                differenceSet.add(v);
            }
        });
        return differenceSet;
    }
    
    return items;
}

子集

Set1()的所有元素,都能在Set2()找到,并且set1.length <= set2.length那么我们就说Set1()Set2()的子集。

function Set(obj) {
    let items = obj || {};
    
    // Set类的基本实现方法 ...
    // 差集
	this.difference = function(otherSet){
        let differenceSet = new Set();

        let values = this.values();
        values.forEach((v)=>{
            if(!otherSet.has(v)){
                differenceSet.add(v);
            }
        });
        return differenceSet;
    }
    
    return items;
}

ES6的Set类操作

我们可以在Set类的原型链上添加并集等集合操作。

// 并集
Set.prototype.union = function (setA,setB) {
    let unionAB = new Set();
    for (let x of setA) unionAB.add(x);
    for (let x of setB) unionAB.add(x);
    return unionAB
};

// 交集
Set.prototype.intersection = function (setA,setB) {
    let intersectionSet = new Set();
    for (let x of setA){
        if(setB.has(x)){
            intersectionSet.add(x);
        }
    }
    return intersectionSet
};

// 差集
Set.prototype.difference = function (setA,setB) {
    let differenceSet = new Set();
    for (let x of setA){
        if(!setB.has(x)){
            differenceSet.add(x);
        }
    }
    return differenceSet
};

// 子集
Set.prototype.subset = function (setA, setB) {
    if (setA.size > setB.size) {
        return false;
    } else {
        for (let x of setA) {
            if (!setB.has(x)) {
                return false;
            }
        }
        return true;
    }
};

let set1 = new Set();
set1.add(11);
set1.add(22);
set1.add(33);
// Set(3) {11, 22, 33}

let set2 = new Set();
set2.add(22);
set2.add(33);
set2.add(44);
// Set(3) {22, 33, 44}

let set3 = new Set();
set3 = set3.union(set1,set2);
// Set(4) {11, 22, 33, 44}

let set4 = new Set();
set4 = set4.intersection(set1,set2);
// Set(2) {22, 33}

let set5 = new Set();
set5 = set5.difference(set1,set2);
// Set(1) {11}

let set6 = new Set();
set6.add(11);
set6.add(22);
// Set(2) {11, 22}

let set7 = new Set();
set7 = set7.subset(set6,set1);
// true

字典

在字典中,存储的是[键,值]对,其中键名是用来查询特定元素的。字典和集合很相似,集合以 [值,值]的形式存储元素,字典则是以[键,值]的形式来存储元素。

字典操作

我们用JavaScript的对象来实现字典,字典操作与Set类很相似,但不同于存储[值,值]对的形式,我们将要存储的是 [键,值]对。

function Dictionary(obj) {
    let items = obj || {};

    // 字典里的元素是不能重复的,所以在add、remove的时候需要判断元素是否已存在
    this.has = function(key){
        // return key in items;
        return items.hasOwnProperty(key);
    };
    // 添加
    this.set = function (key, value) {
        // 添加一个新的值,或更新一个已有的值
        items[key] = value;
        return items;
    };
    // 删除
    this.delete = function (key) {
        if(this.has(key)){
           delete items[key];
           return true;
        }else{
           return false;
        }
    };
    // 获取某个元素
    this.get = function (key) {
        return this.has(key) ? items[key] : undefined;
    };
    // values
    this.values = function () {
        let values = [];
        for(let k in items){
            if(this.has(k)){
                values.push(items[k]);
            }
        }
        return values;
    };
    // keys
    this.keys = function () {
        // 要取出一个对象obj中所有的键名,可以用Object.keys(obj)
        return Object.keys(items);
    };
    // getItems
    this.getItems = function () {
        return items;
    };
    // 清空
    this.clear = function(){ items = {};  };
    // 长度
    this.size = function(){ return Object.keys(items).length; }
}

let dict1 = new Dictionary({'a':'111qq.com','b':'22qq.com','c':'333qq.com'});
dict1.has('c'); // true
dict1.getItems(); // {a: "111qq.com", b: "22qq.com", c: "333qq.com"}
dict1.size(); // 3
dict1.keys(); // (3) ["a", "b", "c"]
dict1.values(); // (3) ["111qq.com", "22qq.com", "333qq.com"]
dict1.get('a'); // "111qq.com"
dict1.set('d','555qq.com') // {a: "111qq.com", b: "22qq.com", c: "333qq.com", d: "555qq.com"}
dict1.delete('d') // true
dict1.getItems() // {a: "111qq.com", b: "22qq.com", c: "333qq.com"}

ES6的Map类

上面的Dictionary类完全可以用ES6的Map类来代替。

let map1 = new Map();
map1.set('a','111qq.com');
map1.set('b','22qq.com');
map1.set('c','333qq.com');
 // Map(3) {"a" => "111qq.com", "b" => "22qq.com", "c" => "333qq.com"}
map1.has('c'); // true
map1.size; // 3
map1.keys(); // MapIterator {"a", "b", "c"}
map1.values(); // MapIterator {"111qq.com", "22qq.com", "333qq.com"}
map1.get('a'); // "111qq.com"

其实,es6的Set类Map类,就是封装好的更语义化更方便的对象,这种对象是用function构造函数实现的。

typeof Set
// "function"
typeof Map
// "function"
typeof WeakMap
// "function"
typeof WeakSet
// "function"

ES6的WeakMap类和WeakSet类

除了Set和Map这两种新的数据结构,ES6还增加了它们的弱化版本,WeakSet和 WeakMap。

基本上,Map和Set与其弱化版本之间仅有的区别是: WeakSet或WeakMap类没有entries、keys和values等方法; 只能用对象作为键。 创建和使用这两个类主要是为了性能。WeakSet和WeakMap是弱化的(用对象作为键),没有强引用的键。这使得JavaScript的垃圾回收器可以从中清除整个入口。

另一个优点是,必须用键才可以取出值。这些类没有entries、keys和values 等迭代器方法,因此,除非你知道键,否则没有办法取出值。

  • WeakMap类也可以用set方法,但不能使用数字、字符串、布尔值等基本数据类 型,需要将名字转换为对象;
  • 搜索、读取和删除值,也要传入作为键的对象。

同样的逻辑也适用于WeakSet类。

let map3 = new WeakMap();
let obj1 = {name:'sun'}, obj2 = {name:'leaf'}, obj3 = {name:'rivers'};
map3.set(obj1,'summer');
map3.set(obj2,'spring');
map3.set(obj3,'autumn');
map3.has(obj1) // true
map3.get(obj2) // "spring"
// WeakMap {{…} => "autumn", {…} => "summer", {…} => "spring"}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值