zoukankan      html  css  js  c++  java
  • ES6类型扩展Set集合和Map集合

    在ES6之前,可选的集合类型非常有限,常见的如数组、对象、类数组对象。数组是数值型索引的数据结构,常被用于创建队列和栈。对象本质上是键值对的集合,但是传统上只能用字符串当作键,这给它的使用带来了很大的限制。如果开发者想要创建非数值型索引的数据结构,或者不想用字符串作为对象的键值,就要另谋他法。es6引入了Set集合与Map集合后,让这一切变得更加容易。

    Set集合

    ES6 提供了新的数据结构 Set,它类似于数组,是一种无重复元素的列表。

    实例属性

    size属性: 返回Set实例的成员总数
    

    实例方法

    add(value): 添加某个值,返回Set结构本身
    has(value): 返回一个布尔值,表示该值是否为Set的成员
    delete(value): 删除某个值,返回一个布尔值,表示删除是否成功
    clear(): 清除所有成员,没有返回值
    

    添加元素

    let set = new Set();
    set.add(1);
    set.add('1');
    console.log(set.size) // 2
    

    调用new Set()创建Set集合,调用add()方法向集合中添加元素。在Set集合中,不会对所存值进行强制的类型转换

    通过add方法向 Set 结构添加成员,不会添加重复的值

    let set = new Set();
    [1,2,3,2,1].forEach(x => set.add(x));
    
    for(let i of set) {
      console.log(i);
    }
    // 1
    // 2
    // 3
    

    如果向Set集合中添加多个对象,则它们之间彼此保持独立

    let set = new Set(), o1 = {}, o2 = {};
    set.add(o1)
    set.add(o2)
    console.log(set.size) // 2
    

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

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

    检测元素

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

    let set = new Set();
    set.add(1);
    console.log(set.has(1)) // true
    console.log(set.has(2)) // false
    

    移除元素

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

    let set = new Set();
    set.add(1);
    set.add(2);
    set.delete(1);
    console.log(set.size) // 1
    set.clear();
    console.log(set.size) // 0
    

    成员遍历

    Set 结构的实例有四个遍历方法,可以用于遍历成员

    keys():返回键名的遍历器
    values():返回键值的遍历器
    entries():返回键值对的遍历器
    forEach():使用回调函数遍历每个成员
    

    keys方法、values方法、entries方法返回的都是遍历器对象。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致

    let set = new Set(['a','b','c']);
    
    for(let item of set.keys()){
      console.log(item)
    }
    // a
    // b
    // c
    
    for(let item of set.values()){
      console.log(item)
    }
    // a
    // b
    // c
    
    for(let item of set.entries()){
      console.log(item)
    }
    // ['a','a']
    // ['b','b']
    // ['c','c']
    

    Set结构部署了Symbol.iterator,所以它的实例默认可遍历

    let set = new Set(['a','b','c']);
    for(let item of set){
      console.log(item)
    }
    // a
    // b
    // c
    

    forEach方法,用于对每个成员执行某种操作,没有返回值。

    let set = new Set(['a','b','c']);
    set.forEach((value, key, set) => { console.log(value,key,set);} )
    //a a ['a','b','c']
    //b b ['a','b','c']
    //c c ['a','b','c']
    

    forEach方法可以接收第二个参数,表示this指向。

    let set = new Set(['a','b','c']);
    let obj = {
      say(value) {
        console.log(value)
      },
      test(setValue) {
        setValue.forEach(function(val){
         this.say(val)
        }, this)
      }
    }
    obj.test(set)
    // a
    // b
    // c
    

    转为数组

    Set集合不能像数组一样,通过下标访问内部元素,但是可以使用展开运算符...转换成数组

    let set = new Set([1,2,3,2,1]), arr = [...set];
    console.log(arr) // [1,2,3]
    

    WeakSet

    如果把对象或数组存储在Set的实例中,只要Set实例中的引用存在,垃圾回收机制就不能释放该对象的内存空间,所以Set类型可以看做是一种强引用的Set集合。

    let set = new Set(), key = {};
    set.add(key);
    console.log(set.size); // 1
    
    // 取消原始引用
    key = null;
    console.log(set.size); // 1
    console.log([...set][0]); {}
    

    当把key赋值为null后,Set集合中对象的引用并没有消失。这并不是缺点,很多时候我们确实需要这样的效果。但有时候又希望当其他引用都不存在时,Set集合能自动取消这些引用。为了解决这个问题,ES6引入了另外一个类型,WeakSet集合(弱引用Set集合)

    创建

    类似于Set集合,可以通过WeakSet构造函数创建WeakSet集合,但是值必须是非null的对象类型。集合支持add()、has()、delete()方法

    let set = new WeakSet(), key = {};
    set.add(key);
    console.log(set.has(key)); // true
    
    // 取消原始引用
    key = null;
    console.log(set.has(key)); // false
    

    当把key赋值为null后,WeakSet集合中对象的引用随之消失。另外,由于WeakSet内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此ES6规定WeakSet不可遍历

    WeakSet集合的使用方式与Set集合类似,可以向集合中添加引用,从中移除引用,也可以检査集合中是否存在指定对象的引用。

    除了一个是弱引用,一个是强引用外,WeakSet集合和Set集合还有以下几点差别:

    1. 在Weakset的实例中,如果向add()、has()和delete()这3个方法传入非对象参数都会导致程序报错
    2. WeakSet集合不可迭代,所以不能被用于for-of循环,也不支持forEach()、keys()、values()、entries()这些方法
    3. WeakSet集合不支持clear方法(因为WeakSet集合无法遍历,所以没法逐一清除)
    4. WeakSet集合不支持size属性

    WeakSet集合和Set集合各有各的用途,应根据实际情况合理选择。

    Map集合

    ES6提供了Map数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应

    Map类型是一种储存着许多键值对的有序列表,其中的键名和对应的值支持所有的数据类型。键名的等价性判断是通过调用Object.is()方法实现的,所以数字5与字符串"5"会被判定为两种类型,可以分别作为独立的两个键出现在程序中,这一点与对象不一样,因为对象的属性名总会被强制转换成字符串类型

    注意: 有一个例外,Map集合中将+0和-0视为相等,这与Object.is()结果不同

    创建Map集合

    可以通过Map构造函数创建Map集合。调用set()方法可以设置键名和键值;如果要从集合中获取信息,可以调用get()方法

    let map = new Map();
    map.set('name','wmui');
    map.set('age',10);
    console.log(map.get('name')); // wmui
    

    在ES6之前,开发者通常使用Object.create()创建类似Map集合的数据结构,这样做有一个明显的缺点,就是多个对象作为键名时,它们之间不是相互独立的

    let map = Object.create(null), o1 = {}, o2 = {};
    map[o1] = 'hello'
    console.log(map[o1]) // hello
    console.log(map[o2]) // hello
    

    由于对象属性的键名必须是字符串,因而这段代码中的o1和o2将被转换为对象对应的默认字符串"[object Object]",所以set[o1]和set[o2]引用的是同一个属性。理论上不同对象作为对象属性的键名应该指向不同属性,但实际上却不是这样。

    现在有了Map集合,就不会再有这样的问题,不同的对象在Map集合中互相独立存在

    let map = new Map(),o1 = {}, o2 = {};
    map.set(o1,'hello');
    console.log(map.get(o1)); // hello
    console.log(map.get(o2)); // undefined
    

    属性

    Map集合也有size属性,返回当前集合中包含的键值对数量

    let map = new Map();
    map.set('name', 'wmui');
    map.set(age, 10);
    console.log(map.size); // 2
    

    方法

    除了set和get方法,Map集合和Set集合有三个通用的方法

    has(key): 检测指定的键名在Map集合中是否已经存在
    delete(key): 从Map集合中移除指定键名及其对应的值
    clear(): 移除Map集合中的所有键值对
    
    let map = new Map();
    map.set('name', 'wmui');
    map.set(age, 10);
    map.delete('name');
    console.log(map.has('name')); // false
    map.clear();
    console.log(map.size); // 0
    

    初始化

    可以向Map构造函数传入数组来初始化一个Map集合,数组中的每个元素都是一个子数组,子数组中包含一个键值对的键名与值两个元素。

    let map = new Map([['name','wmui'],['age',10]]);
    console.log(map.get('name')) // wmui
    console.log(map.get('age')) // 10
    

    虽然看起来有点怪,但这是唯一一种可以准确地呈现键名类型的方式

    遍历

    Map结构原生提供三个遍历器生成函数和一个遍历方法,这点和Set集合一样

    keys():返回键名的遍历器
    values():返回键值的遍历器
    entries():返回所有成员的遍历器
    forEach():遍历 Map 的所有成员
    
    let map = new Map([['name','wmui'],['age',10]]);
    
    for (let key of map.keys()) {
      console.log(key);
    }
    // name
    // age
    
    for (let value of map.values()) {
      console.log(value);
    }
    // wmui
    // 10
    
    for (let item of map.entries()) {
      console.log(item[0], item[1]);
    }
    // name wmui
    // age 10
    
    // 或者
    for (let [key, value] of map.entries()) {
      console.log(key, value);
    }
    // name wmui
    // age 10
    
    // 等同于使用map.entries()
    for (let [key, value] of map) {
      console.log(key, value);
    }
    // name wmui
    // age 10
    

    转为数组

    Map集合转为数组结构,比较快速的方法是使用扩展运算符...

    let map = new Map([['name','wmui'],['age',10]]);
    console.log([...map.keys()]) // ['name', 'age']
    console.log([...map.values()]) // ['wmui',10]
    console.log([...map.entries()]) // [['name,age'],['wmui',10]]
    console.log([...map]) // [['name,age'],['wmui',10]]
    

    结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤

    let m = new Map([['a',1],['b',2],['c',3]]);
    
    let m1 = new Map([...m].filter(([k,v]) => v < 3))
    let m2 = new Map([...m].map(([k,v]) => [v * 2, '_' + k]))
    
    console.log(m1) // {"a" => 1, "b" => 2}
    console.log(m2) // {2 => "_a", 4 => "_b", 6 => "_c"}
    

    Map也有一个forEach方法,与数组的forEach方法类似,也可以实现遍历

    let map = new Map([['name','wmui'],['age',10]]);
    map.forEach(function(value,key,map){
      console.log(value,key,map)
    })
    // wmui name {"name" => "wmui", "age" => 10}
    // 10 age {"name" => "wmui", "age" => 10}
    

    forEach方法还可以接受第二个参数,用来绑定this

    let map = new Map([['name','wmui'],['age',10]]);
    let obj = {
      say(value,key) {
        console.log(value,key)
      },
      test(setValue) {
        setValue.forEach(function(val,key){
         this.say(val,key)
        }, this)
      }
    }
    obj.test(map)
    // wmui name
    // age 10
    

    WeakMap

    WeakSet是引用Set集合,相对地,WeakMap是弱引用Map集合

    使用

    ES6中的WeakMap类型是一种存储着许多键值对的无序列表,列表的键名必须是非null类型的对象,键名对应的值则可以是任意类型。WeakMap的接口与Map非常相似,通过set()方法添加数据,通过get()方法获取数据

    let map = new WeakMap(), key = {};
    map.set(key, 'hello')
    console.log(map.get(key)) // hello
    key = null
    console.log(map.get(key)) // undefined
    

    初始化

    WeakMap集合的初始化过程与Map集合类似,调用WeakMap构造函数并传入一个数组容器

    let o1 = {},o2 = {};
    let map = new WeakMap([[o1,'wmui'],[o2,10]]);
    console.log(map.has(o1)) // true
    console.log(map.has(o2)) // true
    console.log(map.get(o1)) // wmui
    console.log(map.get(o2)) // 10
    

    WeakMap集合与Map集合的区别,类似于WeakSet和Set集合的区别:

    1. 在WeakMap的实例中,get()、set()、has()和delete()这4个方法只支持传入非null的对象值作为参数,其他值会导致程序报错
    2. WeakMap集合不可迭代,所以不能被用于for-of循环,也不支持forEach()、keys()、values()、entries()这些方法
    3. WeakMap集合不支持clear方法
    4. WeakMap集合不支持size属性

    应用

    WeapMap一个比较常用的场合的是存储DOM节点,DOM节点作为键名

    let box = document.querySelector('#box');
    let map = new WeakMap();
    map.set(box, {clickCount: 0});
    box.addEventListener('click', function(){
      let mapData = map.get(box);
      mapData.clickCount++;
      console.log(mapData.clickCount)
    }, false)
    

    这个示例中,为box元素注册了一个点击事件,每次点击会打印点击次数。如果box元素被移除,内部状态会自动消失,不存在内存泄漏风险。

    总结

    Map 和 Set 与其弱化版本之间的区别

    1. WeakSet 或 WeakMap 类没有 entries 、 keys 和 values 等遍历方法
    2. WeakSet 或 WeakMap 类只能用对象作为键
    优秀文章首发于聚享小站,欢迎关注!
  • 相关阅读:
    P1019 单词接龙
    P1219 八皇后
    Find a way
    P1056 排座椅
    P1067 多项式输出
    关于前缀和,A
    洛谷P1223
    洛谷P1803
    洛谷P1208
    常用方法 Excel转换为DataSet
  • 原文地址:https://www.cnblogs.com/yesyes/p/15352391.html
Copyright © 2011-2022 走看看