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
一样,值也是唯一的。
区别
- WeakSet只能存储对象,因此像基本类型就无法存入。
\
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
。