编程技术文章分享与教程

网站首页 > 技术文章 正文

被忽略的Set

hmc789 2024-11-23 16:18:36 技术文章 2 ℃

ES6和ES2015

其实很多同学分不大清楚,ES6和ES2015之间的区别,而且目前已经到了ES2022,笔者这里简单地做一个示意。


ES2015的全称是ECMAScript 2015,它表示的是ES6在2015年6月份发布的第一个版本,因此ES6是个历史性的名词,泛指5.1版本后的JavaScript的标准规范,ES2016,2017以及再后面的年份标志的版本都是ES6的范畴。


ECMAScript和JavaScript之间的关系,就是版本规范和具体实现之间的关系,虽然也有其他实现比如ActionScript等等,由于JavaScript实现使用占比广泛的原因,在一般的场合并不会特意去区分它们。


什么是Set

前面讲到了,ES6的第一个版本早在2015年就已经发布了,其中Map和Set就是其中比较常用的特性,以笔者这么些年的体会,其实很多同学都知道,尤其是有Java后端开发经验的同学,但是大家在实操中,使用Map和Set的情况比较不够。究其原因,还是理解不够深刻,并且JavaScript中使用Array和Object毕竟能满足绝大部分场景。


Set

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。


Set对象是值的集合,可以按照插入的顺序进行迭代,Set中的值是唯一的。


这里讲下MDN中有个很有意思的描述,NaN和undefined是可以存入Set的,但在Set中NaN之间会被认为是重复值,但是NaN!==NaN


我们可以看到,实际的aSet中只有两个值。


Set比较常用的实例方法有add,clear,delete,entires,forEach,has,keys,values等,属性的话就是size,用来返回Set对象中值的个数。这里笔者就不一一进行演示了。我们拿一个大家可能经常会用到的场景来举例。


数组去重

// Use to remove duplicate elements from the array
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)])
// [2, 3, 4, 5, 6, 7, 32]

求数组的并集、交集、补集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let u = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let i = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let d = new Set([...a].filter(x => !b.has(x)));
// Set {1}


WeakSet

仔细研读ES6规范的同学,相信还发现了WeakSet这么个内置对象,接下来我们来一起看看,它是什么,跟Set有什么区别


首先WeakSet对象是一些对象值的集合,并且其中的每个对象值只能出现一次。那就表示跟Set一样,值也是唯一的。

区别

  1. WeakSet只能存储对象,因此像基本类型就无法存入。


  1. WeakSet存储的是弱引用,那就意味着WeakSet中的对象,如果没有其他引用,垃圾回收机制会自动该对象所占用的内存。


实例1

基于弱引用的特点,我们有没有具体使用的场景呢?WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。


那我们思考下,比如DOM对象的使用,我们打开下百度页面,有个元素Id是s_fm

const a=new WeakSet();
//undefined
a.add(document.getElementById('s_fm'));
//WeakSet {div#s_fm.s_form.s_form_login}
a.has(document.getElementById('s_fm'))
//true

我们向a这个WeakSet中添加这个dom的引用,然后使用has实例方法判断下,这个引用是存在的。

然后我们把这个元素从DOM树上删除,然后再试一下

document.getElementById('s_fm').remove();
//undefined
a.has(document.getElementById('s_fm'))
//false

实例2

再来个MDN上的例子,检测循环引用

递归调用自身的函数需要一种通过跟踪哪些对象已被处理,来应对循环数据结构的方法。

为此,WeakSet非常适合处理这种情况

// 对 传入的subject对象 内部存储的所有内容执行回调
function execRecursively(fn, subject, _refs = null){
	if(!_refs)
		_refs = new WeakSet();

	// 避免无限递归
	if(_refs.has(subject))
		return;

	fn(subject);
	if("object" === typeof subject){
		_refs.add(subject);
		for(let key in subject)
			execRecursively(fn, subject[key], _refs);
	}
}

const foo = {
	foo: "Foo",
	bar: {
		bar: "Bar"
	}
};

foo.bar.baz = foo; // 循环引用!
execRecursively(obj => console.log(obj), foo);

在此,在第一次运行时创建WeakSet,并将其与每个后续函数调用一起传递(使用内部参数_refs)。 对象的数量或它们的遍历顺序无关紧要,因此,WeakSet比Set更适合(和执行)跟踪对象引用,尤其是在涉及大量对象时。


好了,今天的分享就到这里,这个系列的下一期我们聊下还有个被大家遗忘的Map。


大家喜欢的话可以点个关注哦,或者关注下公众号:喵爸的小作坊

Tags:

标签列表
最新留言