ES6新特性总结(3)Set集合、Map集合

1 Set集合

Set集合:是一种数据结构,结构类似于数组,且没有重复的值。主要用于数组去重字符串去重

1.1 操作方法

方法含义
add()添加值,返回Set结构本身
delete()删除值,返回一个boolean表示是否删除成功
has()判断该值是否存在,并返回一个boolean
clear()清除所有值,没有返回值

add()使用示例

let s = new Set();
s.add(1); //添加一个值1
console.log(s); //返回s结构本身

结果为:Set(1) { 1 }

let s = new Set();
s.add(1).add(2).add(1); //连续添加三次数,其中1重复添加
console.log(s); //输出Set结构本身

结果为:Set(2) { 1, 2 }

上面的代码中添加了两次1,但是s中只有一个1,因此Set中没有重复的值。根据观察,前两次的结果前面都加了“Set”,而有时候{ }中才是我们想要的结果,因此我们可以使用拓展运算符(…)将Set的值扩展出来

let s = new Set();
s.add(1).add(2).add(1); //为s添加3次数字
console.log(...s) //将Set的值扩展出来

输出结果为:1 2

输出结果是一个序列,我们可以通过加一个[ ]来将其变成数组。

let s = new Set();
s.add(1).add(2).add(1); //为s添加3次数字
console.log([...s]) //输出为数组

输出结果为:[ 1, 2 ]


delete()使用示例

let s = new Set();
s.add(1).add(2); //为Set添加了两个数:1和2
console.log("删除前:", ...s); //输出1 2
s.delete(1); //将1删除掉
console.log("删除后:", ...s); //输出2

输出结果为:
删除前: 1 2
删除后: 2

delete()返回值是判断该数有没有删除成功,因此上一段代码中,s.delete(1)的返回值一定是true。

let s = new Set();
s.add(1).add(2); //为Set添加了两个数:1和2
console.log(s.delete(1)); //输出删除1后的返回值

输出结果为:true

如果删除一个s中不存在的数,那么它的返回值就是false。

let s = new Set();
s.add(1).add(2); //为Set添加了两个数:1和2
console.log(s.delete(3)); //输出删除3后的返回值
console.log(...s); //输出s

结果为:
false
1 2

从上面的结果可以看出,如果删除一个不存在的数,它的返回值是false,但是对原来的Set集合结果并不影响,程序不会报错,会正常输出原来的Set集合。


has()使用示例

let s = new Set();
s.add(1).add(2); //为s添加两个数1和2
console.log(s.has(1)); //判断s中含不含1
console.log(s.has(3)); //判断s中含不含3

结果为:true false

如果该值存在于Set集合中,那么返回值为true;如果该值不存在于Set集合中,那么返回值为false。


clear()使用示例

let s = new Set();
s.add(1).add(2); //为s添加两个数1和2
s.clear(); //清除所有的值
console.log(s);

输出结果为:Set(0) {}

1.2 遍历方法

由于Set只有键值没有键名,也可以说键和值是同一个(键、值相同,可以省略) ,所keys和values返回值相同

let set = new Set();
set.add(1).add(2).add(3); //添加3个数:1,2,3
for (let i of set.keys()) { //遍历set集合的键,keys()代表键
    console.log(i); //输出set集合的键
}

输出结果为:1 2 3

let set = new Set();
set.add(1).add(2).add(3); //添加3个数:1,2,3
for (let i of set.values()) { //遍历set集合的键,keys()代表键
    console.log(i); //输出set集合的键
}

输出结果为:1 2 3

let set = new Set();
set.add("hello").add("world"); //添加两个字符串
for (let i of set.entries()) { //遍历set的键值对,entries()代表键值对
    console.log(i); //输出遍历的结果
}

输出结果为:
[ ‘hello’, ‘hello’ ]
[ ‘world’, ‘world’ ]

使用forEach()遍历Set集合。

let set = new Set();
set.add("hello").add("world"); //为set添加两个字符串
set.forEach((key, val) => { //遍历键和值
    console.log(key + "||" + val); //输出键和值
})

输出结果为:
hello||hello
world||world


Set可以接受一个数组作为参数

let arr = ["小红", "小明", "小强", "小明"];
let set = new Set(arr); //将arr数组作为参数传给set集合
console.log(...set); //输出set集合

结果为:小红 小明 小强

【案例】Set实现并集与交集。

let arr1 = [4, 5, 6];
let arr2 = [5, 6, 7];
let setA = new Set(arr1);
let setB = new Set(arr2);
//求并集,将两个集合合并在一起,去掉多余的重复的数字
let bingji = new Set([...setA, ...setB]);
console.log(...bingji); //输出并集的结果
//求交集,将两个集合合并在一起,求重复的数字,使用filter过滤器得到结果
//在setA中过滤出与setB共同含有的值
let jiaoji = new Set([...setA].filter(val => setB.has(val)));
console.log(...jiaoji); //输出交集的结果

结果为:
4 5 6 7
5 6

1.3 WeakSet

WeakSet只能是对象的集合,而不能是任何类型的任意值。

WeakSet集合中对象的引用为弱引用。如果没有其他的对WeakSet中对象的引用,那么这些对象会被当成垃圾回收掉。

这也意味着WeakSet中没有存储当前对象的列表。正因为这样,WeakSet 是不可枚举的。即WeakSet中对对象的引用不会被考虑进垃圾回收机制,即只要没有其他的对象引用该对象,则该对象就会被回收,而不管它在不在 WeakSet。

WeakSet 支持 add,has 和 delete 方法,但不支持 size 和 keys(),并且不可迭代,无法遍历

为weakSet添加对象,查看其返回值:

let jack = { name: "jack" }; //jack是一个对象
let weakSet = new WeakSet();
weakSet.add(jack); //为weakSet添加一个对象
console.log(weakSet.has(jack)); //判断weakSet是否含有jack,并输出返回值

得到结果为:true

删除刚刚添加的对象,查看返回值:

let jack = { name: "jack" }; //jack是一个对象
let weakSet = new WeakSet();
weakSet.add(jack); //为weakSet添加一个对象
weakSet.delete(jack); //将jack从weakSet中删除掉
console.log(weakSet.has(jack)); //判断weakSet是否含有jack,并输出返回值

得到结果:false

为WeakSet添加除了对象之外的内容,查看其结果:

let jack = { name: "jack" }; //jack是一个对象
let weakSet = new WeakSet();
weakSet.add(1); //为weakSet添加除对象之外的值

程序会报错:TypeError: Invalid value used in weak set,因为WeakSet中只能存放对象。

WeakSet 的应用场景/好处:用于存储DOM节点,而不用担心这些节点从文档移除时会引发内存泄露。

2 Map集合

2.1 Map概述

JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串——值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。

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

注意:
1、有一个例外,Map集合中将+0和-0视为相等,与Object.is()结果不同。
2、如果需要“键值对”的数据结构,Map比Object更合适,具有极快的查找速度

2.2 基本方法和属性

1、属性:size,返回Map中的元素个数

2、基本方法

方法含义
set()给Map添加数据,返回添加后的Map (给已存在的键赋值会覆盖掉之前的值)
get()获取某个key的值,返回key对应的值,没有则返回undefined
has()检测是否存在某个key,返回布尔值
delete()删除某个key及其对应的value,返回布尔值,成功:true; 失败:false
clear()清除所有的值,返回 undefined

size、set()使用示例

let map = new Map();
map.set("name", "橘猫吃不胖"); //为map添加数据
map.set("age", 2); //为map添加数据
console.log(map.size); //输出map中元素个数(长度)
console.log(map); //输出添加数据后的map

输出结果为:
2
Map(2) { ‘name’ => ‘橘猫吃不胖’, ‘age’ => 2 }

get()使用示例

let map = new Map();
map.set("name", "橘猫吃不胖"); //为map添加数据
map.set("age", 2); //为map添加数据
console.log(map.get("name")); //输出name对应的值
console.log(map.get("address")); //当没有该键时返回undefined

输出结果为:
橘猫吃不胖
undefined

has()使用示例

let map = new Map();
map.set("name", "橘猫吃不胖"); //为map添加数据
map.set("age", 2); //为map添加数据
console.log(map.has("name")); //判断map中是否含有name
console.log(map.has("address")); //判断map中是否含有address

输出结果为:
true
false

delete()使用示例

let map = new Map();
map.set("name", "橘猫吃不胖"); //为map添加数据
map.set("age", 2); //为map添加数据
console.log(map.delete("name")); //删除map中的name
console.log(map.delete("address")); //删除map中的address

输出结果为:
true
false

clear()使用示例

let map = new Map();
map.set("name", "橘猫吃不胖"); //为map添加数据
map.set("age", 2); //为map添加数据
console.log(map); //输出添加值之后的map
console.log(map.clear()); //输出清除所有值之后的返回值
console.log(map); //输出清除值之后的map

输出结果为:
Map(2) { ‘name’ => ‘橘猫吃不胖’, ‘age’ => 2 }
undefined
Map(0) {}

2.3 遍历方法

注意:Map的遍历顺序就是插入顺序

方法含义
keys()获取Map的所有key
values()获取Map的所有值
entries()获取Map所有成员
forEach()遍历Map的所有成员
//创建一个map集合,传入一个二维数组
const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
console.log(map); 

输出结果为:Map(2) { ‘F’ => ‘no’, ‘T’ => ‘yes’ }

keys()使用示例

//创建一个map集合,传入一个二维数组
const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
for (let key of map.keys()) { //遍历map的键
    console.log(key); //输出map的键
}

输出结果为:F T

values()使用示例

//创建一个map集合,传入一个二维数组
const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
for (let value of map.values()) { //遍历map的值
    console.log(value); //输出map的值
}

输出结果为:
no
yes

entries()使用示例

//创建一个map集合,传入一个二维数组
const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
for (let item of map.entries()) { //遍历map所有成员
    console.log(item); //输出map所有成员
}

输出结果为:
[ ‘F’, ‘no’ ]
[ ‘T’, ‘yes’ ]

或者:

//创建一个map集合,传入一个二维数组
const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
for (let [key, value] of map.entries()) { //遍历map成员
    console.log(key, value); //输出map成员
}

输出结果为:
F no
T yes

或者:

const map = new Map([
    ["F", "no"],
    ["T", "yes"]
])
// 等同于使用map.entries()
for (let [key, value] of map) { //遍历map成员
    console.log(key, value); //输出map成员
}

输出结果为:
F no
T yes

2.4 转为数组

Map结构转为数组结构,比较快速的方法是使用扩展运算符(…)。

//创建一个map集合
const map = new Map([
    [1, "one"],
    [2, "two"],
    [3, "three"]
])
//将map的键转化为一个数组
console.log(...map.keys()); //1 2 3
//将map的值转化成一个数组
console.log(...map.values()); //one two three
//将map对象转化成一个数组
console.log(...map.entries()); //[ 1, 'one' ] [ 2, 'two' ] [ 3, 'three' ]
//将map转化成一个数组
console.log(...map); //[ 1, 'one' ] [ 2, 'two' ] [ 3, 'three' ]

2.5 Map的遍历和过滤

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

const map0 = new Map();
map0.set(1, "a"); //为map0添加数据
map0.set(2, "b"); //为map0添加数据
map0.set(3, "c"); //为map0添加数据

//filter()的使用
const map1 = new Map(
    //将map0中键小于3的过滤出来
    [...map0].filter(([k, v]) => k < 3)
);
console.log(map1); //Map(2) { 1 => 'a', 2 => 'b' }

//map()的使用
const map2 = new Map(
    //将map0中的成员,键变成2倍,值的前面加"_"
    [...map0].map(([k, v]) => [k * 2, "_" + v])
)
console.log(map2); //Map(3) { 2 => '_a', 4 => '_b', 6 => '_c' }

2.6 forEach()

const map = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
map.forEach((value, key, map) => {
    console.log(value, key, map);
})

输出结果为:
one 1 Map(3) { 1 => ‘one’, 2 => ‘two’, 3 => ‘three’ }
two 2 Map(3) { 1 => ‘one’, 2 => ‘two’, 3 => ‘three’ }
three 3 Map(3) { 1 => ‘one’, 2 => ‘two’, 3 => ‘three’ }

2.7 WeakMap

WeakMap是弱引用Map集合,也用于存储对象的弱引用。WeakMap集合中的键名必须是一个对象,如果使用非对象键名会报错。

集合中保存的是这些对象的弱引用,如果在弱引用之外不存在其他的强引用,引擎的垃圾回收机制会自动回收这个对象,同时也会移除WeakMap集合中的键值对。但是只有集合的键名遵从这个规则,键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制。

1、使用WeakMap集合
ES6中的WeakMap类型是一种存储着许多键值对的无序列表,列表的键名必须是非null类型的对象,键名对应的值则可以是任意类型。

WeakMap的接口与Map非常相似,通过set()方法添加数据,通过get()方法获取数据。

let map = new WeakMap();
const o1 = {}; //o1是一个空对象
const o2 = function () { }; //o2是一个空函数
map.set(o1, o2); //给map添加数据,键和值可以是任意对象,甚至是另一个WeakMap对象
map.set(o2, "橘猫吃不胖"); //为map添加数据
console.log(map.get(o1)); //[Function: o2]
console.log(map.get(o2)); //橘猫吃不胖

WeakMap集合不支持size属性,从而无法验证集合是否为空。

2、WeakMap集合支持的方法

方法含义
has()检测给定的键在集合中是否存在
delete()移除指定的键值对

has()使用示例

let map = new WeakMap();
const o1 = {}; //o1是一个空对象
const o2 = function () { }; //o2是一个空函数
map.set(o1, o2); //给map添加数据,键和值可以是任意对象,甚至是另一个WeakMap对象
map.set(o2, "橘猫吃不胖"); //为map添加数据
console.log(map.has(o1)); //判断map中是否含有o1,true
console.log(map.has(o3)); //判断map中是否含有o3,程序报错,ReferenceError: o3 is not defined

delete()使用示例

let map = new WeakMap();
const o1 = {}; //o1是一个空对象
const o2 = function () { }; //o2是一个空函数
map.set(o1, o2); //给map添加数据,键和值可以是任意对象,甚至是另一个WeakMap对象
map.set(o2, "橘猫吃不胖"); //为map添加数据
console.log(map.delete(o1)); //删除map中的o1,删除成功返回true
console.log(map.has(o1)); //判断map中是否含有o1

输出结果为:true false

3、WeakMap集合的用途

  • 储存DOM元素
    <button id="btn">点击</button>
    <script>
        //获取button元素
        let myElement = document.getElementById("btn");
        let myWeakmap = new WeakMap();
        //为myWeakmap添加数据
        myWeakmap.set(myElement, { timesClicked: 0 });
        //为按钮添加click事件
        myElement.addEventListener("click", function () {
            //获取myWeakmap键为myElement的值
            let logoData = myWeakmap.get(myElement);
            //点击一次加一次1
            logoData.timesClicked++;
            //输出点击的次数
            console.log(logoData.timesClicked);
        })
    </script>

在这里插入图片描述
代码中,myElement是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在WeakMap里,对应的键名就是myElement。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险

  • 注册监听事件的listener对象,就很合适用WeakMap实现。
    <button class="element1">按钮1</button>
    <button class="element2">按钮2</button>
    <script>
        //获取元素
        let element1 = document.querySelector(".element1");
        let element2 = document.querySelector(".element2");
        const listener = new WeakMap();
        //为listener添加两个数据
        listener.set(element1, "handler1");
        listener.set(element2, "handler2");
        //为element1添加click事件
        element1.addEventListener("click", function () {
            console.log(listener.get(element1));
        })
        //为element2添加click事件
        element2.addEventListener("click", function () {
            console.log(listener.get(element2));
        })
    </script>

在这里插入图片描述
监听函数放在WeakMap里面。一旦 DOM 对象消失,跟它绑定的监听函数也会自动消失。

  • 部署私有属性
    我们创建对象的时候,通常写一个构造函数。
function Person(name) {
    this._name = name;
}
Person.prototype.getName = function () {
    return this._name;
}

但这时,创建一个Person 对象的时候,我们可以直接访问name属性:

const person = new Person("橘猫吃不胖");
console.log(person._name); //橘猫吃不胖

我们不想让用户直接访问name属性, 直接使用下面的方法将name包装成私有属性。

let Person = (function () {
    let privateData = new WeakMap();
    function Person(name) { //定义一个函数,参数为name
        privateData.set(this, { name: name }); //为privateData设置数据
    }
    Person.prototype.getName = function () { //设置getName函数
        return privateData.get(this).name; //获得privateData中name键的值的name
    }
    return Person;
}()); //()()该函数设置之后就调用
const person = new Person("橘猫吃不胖");
console.log(person.getName());
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值