文章目录
说明:易混淆知识点是指不同于基本数据结构的知识点。
Set
Set在JS中是表示集合的,很好记。
元素唯一性
Set
具有集合的特征:元素唯一性。
放在JS中,它实际上是键的唯一性,因为每个元素都是键值对,只不过键完全等于值。
唯一性的含义:类别、值、地址完全相等。和全等符号===
不同之处只有一个:NaN
是唯一的。也就是说:
let a = NaN, b = NaN;
a === b // false
let set = new Set();
set.add(a);
set.add(b);
console.log(set); // 只有一个NaN,保持唯一性了
此外,对于引用类型(非原始类型,如Array
、Object
),即使类别、值相等,它们也不一定是一样的,因为引用可以理解为指针,它们指向的空间存放的数据虽然完全相同,但是它们的空间地址不同,不能叫一样。用Java的概念去理解的话,就是对象的唯一性id不一样。
let a = {};
let b = {};
let c = a;
let set = new Set();
set.add(a);
set.has(b); // false
set.has(c); // true,因为地址相同,即对象id相同,即是同一个对象!
set.add(b); // 此时set中含有两个空对象
此外,Set
在加入元素时,不会改变它的类型,否则就不能进行唯一性判断了。
元素遍历
由于JS的对象的复杂性,并不是所有元素都会在遍历方法中被遍历到。
keys()
和values()
方法返回迭代器,行为基本完全一样,因为Set
的键和值是相等的。而entries()
确实返回键值对的迭代器,只不过键和值相等,所以基本上用不上它。
Set
可以直接用for ... = of ...
遍历,它执行的是values()
方法。
Set
可以用forEach
遍历。
Set
的遍历顺序等于元素加入顺序。当加入时判断唯一性失败,并不执行覆盖操作,也就是说以第一次添加的顺序为准。
Set
可以用扩展运算符...
解构,因为它实际上是执行for ... of ...
。
将Set
解构为数组后,可以模拟交集、并集和差集的操作,可以使用Array
的方法,如filter()
。
特殊方法
集合操作union()
, difference()
, intersection()
, symmetricDifference()
, isSubsetOf()
, isSupersetOf()
, isDisjointOf()
。
WeakSet
weak
部分是它和Set
的区别所在。它的键只能是引用类型或Symbol类型,不能是null
,而且都是弱引用,可以被垃圾回收。因此,WeakSet
不可遍历,也没有size
属性。
weak
并不是指弱唯一性!
Symbol
不是引用类型!但是它却很像!
需要注意的是,如果用一个狭义的一维数组构造一个WeakMap
对象,会失败,因为它遍历所得的元素都是基本数据类型,不允许加入WeakMap
。但如果是一个狭义的二维数组,或者一个元素都是引用类型的数组,则可以用于构造WeakMap
。
它的应用场景是可以存储DOM结点,不用担心内存泄露。
Map
对比
它和Set
的区别是键值可以不相等,然而键仍然是唯一的。判断唯一性时,和Set
一样:只要严格相等,则认为是相等的;例外就是NaN
只能在键中出现一次。
它和Object
的区别是键可以是除了字符串意外的任何其他类型,而Object
的键只能是字符串或Symbol
。
细节上的区别:
- Map可以有一个null键。
- Object内部属性的顺序和插入顺序无关。
- Object没有size或length属性,但可以通过遍历方法如
keys()
来获得内部属性的个数。
构造
Map
接收遍历的结果为含有两个元素的数组的类型作为参数,进行新Map
的构造。例如,new Set(['a': 1, 'b':2])
可以用于构造Map
,一个Object
可以用来构造Map
,一个二维数组可以用于构造Map
。当插入时出现了唯一性冲突,则新插入的值会覆盖该键原本的值。这个行为和Java的一样。
读取不存在的键,返回undefined
。
set()
方法返回原Map
对象,因此可以链式调用。
遍历
Map
的默认遍历方法是entries()
。
WeakMap
它的特性非常类似于WeakSet
:null
以及其他非引用和Symbol
类型不能作为键,对于键都是弱引用。
WeakRef(ES2021)
直接创建对象的弱引用。
let origin = {};
let weak_origin_ref = new WeakRef(origin);
let check = weak_origin_ref.deref(); // 如果已经被回收,返回undefined
if (check) {
// 如果没有被回收,deref()返回原始对象
}
WeakRef
可以用作缓存,如果没有被回收,就直接取值,否则重新加载。
一旦使用WeakRef()
创建了原始对象的弱引用,那么在本轮事件循环,原始对象肯定不会被清除,只会在后面的事件循环才会被清除。
FinalizationRegistry(ES2021)
用于注册一个对象被销毁的事件监听。每个被注册的对象只会形成弱引用。
语法:let a = new FinalizationRegistry(回调函数); a.register(一个对象, 回调函数的参数);
。
Object类型的多个遍历方法区分
for ... in ...
:遍历自身的和继承的可枚举属性名,不含Symbol类型的属性名。Object.keys(obj)
:遍历自身的可枚举属性名,不含Symbol类型,不含继承的。Object.getOwnPropertyNames(obj)
:遍历自身的所有属性名,Symbol类型除外,包含不可枚举类型,不包含继承属性。Object.getOwnPropertySymbols(obj)
:只返回自身的Symbol类型属性名。Reflect.ownKeys(obj)
:返回自身的所有属性名,包含Symbol类型,和不可枚举类型,不包含继承。
遍历属性的顺序不是插入属性的顺序!而是先数值类型(实际上是只包含数字的字符串),然后字符串类型的属性名,然后是Symbol类型的属性名。