Set、WeakSet
Set
Set
对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set中的元素只会出现一次,即 Set 中的元素是唯一的。
NaN
和undefined
都可以被存储在Set 中, NaN
之间被视为相同的值(NaN被认为是相同的,尽管 NaN !== NaN)。
创建一个Set对象:
new Set([iterable])
iterable为可选参数,如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set中。
如果不指定此参数或其值为null
,则新的 Set为空。
属性:
size
返回Set对象中元素的个数。
var mySet = new Set();
mySet.add(1);
mySet.add(5);
mySet.add("some text")
mySet.size; // 3
方法:
Set.prototype.add(value)
在Set
对象尾部添加一个元素。返回该Set
对象。
Set.prototype.clear()
移除Set
对象内的所有元素。
Set.prototype.delete(value)
移除Set
中与这个值相等的元素,返回Set.prototype.has(value)
在这个操作前会返回的值(即如果该元素存在,返回true
,否则返回false
)。Set.prototype.has(value)
在此后会返回false
。
Set.prototype.entries()
返回一个新的迭代器对象,该对象包含Set
对象中的按插入顺序排列的所有元素的值的[value, value]
数组。为了使这个方法和Map
对象保持相似, 每个值的键和值相等。
Set.prototype.forEach(callbackFn[, thisArg])
按照插入顺序,为Set对象中的每一个值调用一次callBackFn。如果提供了thisArg
参数,回调中的this
会是这个参数。
Set.prototype.has(value)
返回一个布尔值,表示该值在Set
中存在与否。
Set.prototype.keys()
与values()
方法相同,返回一个新的迭代器对象,该对象包含Set
对象中的按插入顺序排列的所有元素的值。
Set.prototype.values()
返回一个新的迭代器对象,该对象包含Set
对象中的按插入顺序排列的所有元素的值。
Set.prototype[@@iterator]()
返回一个新的迭代器对象,该对象包含Set
对象中的按插入顺序排列的所有元素的值。
Set与Array
let myArray = ["value1", "value2", "value3"];
// 用Set构造器将Array转换为Set
let mySet = new Set(myArray);
mySet.has("value1"); // returns true
// 用...(展开运算符)将Set转换为Array
console.log([...mySet]); // 与myArray完全一致
实现数组去重
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]
WeakSet
WeakSet
对象是一些对象值的集合, 并且其中的每个对象值都只能出现一次。
new WeakSet([iterable]);
如果传入一个可迭代对象作为参数, 则该对象的所有迭代值都会被自动添加进生成的 WeakSet
对象中。null 被认为是 undefined。
WeakSet没有属性,只有三个实例方法。
方法:
add(value)
add()
方法在 WeakSet
对象的最后一个元素后添加新的对象。
ws.add(value);
value必须。 将对象添加进 WeakSet
集合中。
var ws = new WeakSet();
ws.add(window); // 添加 window 对象进 WeakSet 中
ws.has(window); // true
// Weakset 仅取得对象作为参数
ws.add(1);
// 结果为 "TypeError: Invalid value used in weak set" 在 Chrome 浏览器中
// 并且 "TypeError: 1 is not a non-null object" 在 Firefox 浏览器中
delete()
delete()
方法从 WeakSet 对象中
移除指定的元素.
ws.delete(value);
value为必须。从 WeakSet
对象中移除的对象。
var ws = new WeakSet();
var obj = {};
ws.add(window);
ws.delete(obj); // 返回 false。因为找不到要删除的obj
ws.delete(window); // 返回 true。成功地移除了元素
ws.has(window); // 返回 false。因为 WeakSet 中已经不存在 window 对象
has()
has()
方法根据 WeakSet
是否存在相应对象返回布尔值。
ws.has(value);
value必须。 测试 WeakSet
中是否存在该对象。
如果 WeakSet
对象中存在指定的元素,返回 true
;否则返回 false
。
与Set的区别
它和 Set
对象的区别有两点:
- 与
Set
相比,WeakSet
只能是对象的集合,而不能是任何类型的任意值。 -
WeakSet
持弱引用:集合中对象的引用为弱引用。 如果没有其他的对WeakSet
中对象的引用,那么这些对象会被当成垃圾回收掉。 这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet
是不可枚举的。
检测循环引用
递归调用自身的函数需要一种通过跟踪哪些对象已被处理,来应对循环数据结构的方法。
为此,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
更适合(和执行)跟踪对象引用,尤其是在涉及大量对象时。