前两章我们一起探讨了 Map 和 WeakMap。这两节我们就来讨论下 Set 和 WeakSet 吧。从中我们也可以对比它们的使用场景是如何的。
Set 官方描述与实例创建
原文如下:
Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set's collection. Distinct values are discriminated using the SameValueZero comparison algorithm.
Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Set objects specification is only intended to describe the required observable semantics of Set objects. It is not intended to be a viable implementation model.
翻译如下:
Set 对象是 ECMAScript 语言值的集合。不同的值只能作为集合集合的元素出现一次。内部使用 SameValueZero 比较算法区分不同的值。
必须使用散列表或其他机制实现集合对象,平均来说,提供对集合中元素数量具有亚线性的访问时间。此 Set 对象规范中使用的数据结构仅用于描述 Set 对象的必需的可观察语义。它不是一个可行的内置系统模型。
前面我们学习了 Map 实例的创建方式,Set 的创建方式也是一样的,一般是采用以下两种形式:
let set = new Set();
set.add('a').add('b').add('c');
// 或者
let set = new Set(['a','b','c']);
与 Map 一样,这里我们必须注意:使用第二种方式创建的时候,[]
是绝对不可以省略的,否则将会输出意想不到的结果。
// 错误
let set = new Set('a','b','c'); // {"a"}
// 正确
let set = new Set(['a','b','c']); // {"a", "b", "c"}
当我们省略 []
时,实际上,Set 的实例将只输出第一个元素,忽略后面所有的元素。
另外一点需要格外注意的是:Set 中不能有重复的元素,否则将会忽略后面相同的元素。
let set = new Set();
set.add('foo');
set.size; // 1
set.add('foo');
set.size; // 1
上述例子中,我们可以看出,确实只输出了第一个元素。
但对象却不是这样:
let set = new Set();
set.add({});
set.size; // 1
set.add({});
set.size; // 2
对象被认为是两个元素,这也很好理解。因为我们使用 ===
符号对比两个对象,返回的也是 false。
{} === {}; // false
好了,下面这个例子展示了 Set 与 Array 的区别:
let arr = [3, 5, 2, 2, 5, 3, 5];
let unique = [...new Set(arr)]; // [3, 5, 2]
当我们使用展开操作符时,按照迭代的顺序进行展开,但如果有重复的元素,后面的元素将会被忽略。
Set 的原型方法
(1)Set.prototype.add ( value )
概述:add() 方法用来向一个 Set 对象的末尾添加一个指定的值。
示例:
let set = new Set();
set.add('a');
// 也可使用链式写法
set.add('a').set('b').set('c');
(2)Set.prototype.clear ( )
概述:clear() 方法用来清空一个 Set 对象中的所有元素。
示例:
let set = new Set();
set.add('a');
set.size; // 1
set.clear();
set.size; // 0
(3)Set.prototype.delete ( value )
概述:delete() 方法可以从一个 Set 对象中删除指定的元素。
示例:
let set = new Set(['a','b','c']);
set.delete('a');
console.log(set); // {"b", "c"}
(4)Set.prototype.entries ( )
概述:entries()方法返回一个新的迭代器对象 ,这个对象的元素是类似 [value, value] 形式的数组,value 是集合对象中的每个元素,迭代器对象元素的顺序即集合对象中元素插入的顺序。
由于 Set 不是键值对的形式,因此没有 [key,value] 的写法,因此使用 [value, value] 来代替,以保持一致性。
示例:
let set = new Set(['a','b','c']);
for (let val of set.entries()) {
console.log(val);
}
// ["a", "a"]
// ["b", "b"]
// ["c", "c"]
(5)Set.prototype.forEach ( callbackfn [ , thisArg ] )
概述:forEach() 方法根据集合中元素的顺序,对每个元素都执行提供的 callback 函数一次。
示例:
let set = new Set([1, 2, 3]);
set.forEach((x) => {
console.log(x * x);
});
// 1
// 4
// 9
(6)Set.prototype.has ( value )
概述:**has() ** 方法返回一个布尔值来指示对应的值是否存在 Set 对象中。
示例:
let set = new Set([1, 2, 3]);
set.has(1); // true
set.has(4); // false
(7)Set.prototype.keys ( )
概述:values() 方法返回一个 Iterator 对象,这个对象以插入Set 对象的顺序包含了原 Set 对象里的每个元素。
示例:
let set = new Set([1, 2, 3]);
for (let key of set.keys()) {
console.log(key);
}
// 1
// 2
// 3
(8)Set.prototype.size
概述:Size 方法将会返回 Set 对象中元素的个数。
示例:
let set = new Set([1, 2, 3]);
set.size; // 3
(9)Set.prototype.values ( )
概述:values() 方法返回一个 Iterator 对象,这个对象以插入Set 对象的顺序包含了原 Set 对象里的每个元素。
该方法返回的值与前面介绍的 keys() 方法返回值是一样的。
示例:
let set = new Set([1, 2, 3]);
for (let val of set.values()) {
console.log(val);
}
// 1
// 2
// 3
交集、并集与差集
原生的 JavaScript 中并没有这个概念,而在使用 Set 后,我们可以实现这 3 种形式的数学方法。
(1)并集(Union )
所谓并集(∪)就是以属于 A **或 **属于 B 的元素为元素的集合成为 A 与 B 的并(集)。
我们学习了前面的知识知道了 Set 中的元素是不可以相同的,因此这是很简单就可以实现的:
let a = new Set([1,2,3]);
let b = new Set([4,3,2]);
let union = new Set([...a, ...b]); // {1, 2, 3, 4}
(2)交集(Intersection )
交集(∩)的意思是以属于 A 且 属于 B 的元素为元素的集合成为 A 与 B 的交(集)
示例如下:
let a = new Set([1,2,3]);
let b = new Set([4,3,2]);
let intersection = new Set(
[...a].filter(x => b.has(x))
);
// {2, 3}
(3)差集(Difference )
差集(\)的意思是以属于 A 而不属于 B 的元素为元素的集合成为 A 与 B 的差(集)。
示例如下:
let a = new Set([1,2,3]);
let b = new Set([4,3,2]);
let difference = new Set(
[...a].filter(x => !b.has(x))
);
// {1}
由于 Set 对象本身的意思就是集合。所以我们也可以实现集合的这三个数学表达。不知道大家有没有 get 到呢?是不是又学到了新技能?
总结
目前为止,我们将 Set 也已经讲完了。其实我们回头看看,JavaScript 很难学吗?当我们搞明白了内部机制后,这些方法不都是一样的吗?估计这一篇大家看起来应该也会很快的。
我们下一章讲讲 WeakSet,这样,我们今天一天就将这 4 个 ES6 中新增的对象就讲完了。