本文详细介绍ES6新增的Set和WeakSet数据结构。Set用于存储唯一值,支持并集、交集等集合运算及多种遍历方式;WeakSet仅能存储对象引用,具备弱引用特性,适合存储DOM节点以避免内存泄漏。文章通过具体代码示例解析两者的构造函数、常用方法及遍历操作。
Set与WeakSet概述
ES6引入了两种新的数据结构:Set和WeakSet。Set类似于数组,但其成员值具有唯一性,不允许重复。WeakSet同样存储不重复的值,但它仅用于存放对象引用。
Set数据结构详解
一、Set的使用方法
(1) Set提供了一个内置构造函数,用于创建Set实例。
var s = new Set();
[2,2,2,5,8,16,2,1].map(x => s.add(x))
for(i of s){console.log(i)}
//2,5,8,16,1
(2) Set()构造函数可以接收一个数组作为参数,用于初始化Set实例。
var s = new Set([1,2,3,4,2,4,3]);
[...s]
//[1,2,3,4]
注意:向Set中添加值时不会发生类型转换,因此数字5和字符串"5"被视为两个不同的值。Set内部判断两个值是否相等使用的是严格相等运算符(===),这意味着两个空对象总是不相等的。唯一的例外是NaN,尽管严格相等运算符认为NaN不等于自身,但Set会将它们视为同一个值。
let set = new Set();
set.add({})
set.size//1
set.add({})
set.size//2
上述代码表明,由于两个空对象在严格相等比较中不相等,因此它们被视为Set中的两个不同成员。
(3) Set的方法和属性
(3.1) Set的属性
Set.prototype.size:返回Set实例中包含的成员数量。\n\nSet.prototype.constructor:返回默认构造Set的函数。
(3.2) Set的操作函数
add(value):添加某个值,返回Set结构本身。\n\ndelete(value):删除某个值,返回一个布尔值,表示删除是否成功。\n\nhas(value):返回一个布尔值,表示参数是否为Set的成员。\n\nclear():清除所有成员,没有返回值。
var set = new Set();\n\nset.add(1).add(2).add(22).add(22);\n\nset.size//3
set.hae(22)//true\n\nset.has(4)//false\n\nset.delete(2)//true
(3.3) Set遍历操作
Set提供了四种遍历方法,可用于遍历其成员。\n\nkeys():返回一个键名的遍历器。\n\nvalues():返回一个值的遍历器。\n\nentries():返回一个键值对的遍历器。\n\nforEach():使用回调函数遍历每个成员。
注意:由于Set没有键名,只有值,因此keys()和values()返回的结果是相同的。
let set = new Set(['red','green','blue']);
for(let item of set.keys()){
console.log(item);
}
//red,green,blue
for(let item of set.values()){
console.log(item);
}
//red,green,blue
for(let item of set.entries()){
console.log(item);
}
//["red","red"]
//["green","green"]
//["blue","blue"]
//所以,entries方法返回的遍历器同时包括键名和值,所以每次输出的是一个数组。其实成员都是完全一样的。
注意:Set默认是可遍历的,其默认遍历器生成函数就是它的values方法。这意味着可以省略values方法,直接使用for...of进行遍历。
var set = new Set([1,2,3,4]);
for(let x of set){
console.log(x);
}
//1
//2
//3
//4
如果使用扩展运算符(...),其内部使用for...of循环,因此也可以用于Set结构。
let set = new Set(['red','green','blue']);
let arr = [...set];
//['red','green','blue'];
(3.4) Set实现并集,交集,差集
let set1 = new Set([1,2,3,4,5,6]);
let set2 = new Set([4,5,6,7,8,9]);
//并集
let union = new Set([...set1,...set2]);
//[1,2,3,4,5,6,7,8,9]
//交集
let intersect = new Set([...set1].filter(x => b.has(s)));
//[4,5,6]
//差集
let intersect = new Set([...set1].filter(x => !b.has(s)));
//[1,2,3,4]
(3.5) Set实现forEach的使用
let set = new Set([1,2,3,4,5,6]);
set.forEach(value,key)=>consloe.log(vlaue+1);
//2
//3
//4
//5
//6
//7
注意:forEach方法的参数是一个处理函数,该函数依次接收(值,键名,集合本身)作为参数。此外,forEach方法还有第二个参数,用于绑定this对象。
WeakSet数据结构详解
二、WeakSet使用方法
WeakSet类似于Set,也是存储不重复值的集合。但它只能用于存储对象,不能存储其他类型的值。\n\nWeakSet是一个构造函数,可以接受数组或类似数组的对象作为参数(实际上,任何具有iterable接口的对象都可以作为WeakSet的参数)。该数组的所有成员都会自动成为WeakSet实例对象的成员。\n\nvar a = new [[1,2],[3,4]]; \n\nvar ws = new WeakSet(a);
var ws = new WeakSet();
ws.add(1);//TypeError:Invalid value used in weak set
ws.add(Symbol);//TypeError:Invalid value used in weak set
尝试添加一个数值和一个Symbol,结果同时报错。
WeakSet结构有以下方法:\n\nWeakSet.protoptype.add(value):向WeakSet实例添加一个新成员。\n\nWeakSet.protoptype.delete(value):删除WeakSet实例指定成员。\n\nWeakSet.protoptype.has(value):返回一个布尔值,表示某个值是否在WeakSet实例中。
var ws = new WeakSet();
var obj = {};
var foo = {};
ws.add(window);
ws.add(obj);
ws.has(window);//true
ws.has(foo);false
ws.delete(window);//true
ws.has(window);//false
WeakSet不能遍历,是因为其成员都是弱引用,随时可能被垃圾回收机制回收,遍历无法保证成员的存在。例如,可能刚刚遍历结束,成员就被取走了。WeakSet的一个主要用途是存储DOM节点,这样当这些节点从文档中移除时,不会引起内存泄漏。