原文:http://www.nczonline.net/blog/2012/09/25/ecmascript-6-collections-part-1-sets/
译者注:因为英文中的collection和Set在中文中都叫集合,为了防止混淆,本文把collection翻译成集合,表示所有的集合类型,Set不译,表示Set类型,小写的set指的是一个Set类型的对象实例
在JavaScript的历史中,长期以来只有一种集合类型,那就是数组(Array).在JavaScript中,数组不仅包含了其他语言中数组的功能,还可以用来模拟队列和栈.但数组也有不足:由于数组的索引只能是数字,所以在需要使用一个非数字索引的时候,开发人员必须使用对象(Object)来代替,可还是有问题.终于,ECMAScript 6中引入了几种新的集合类型,能够让我们更好更方便的存储有序数据.
Set
虽然在JavaScript中,一直没有Set类型,但如果你熟悉Java,Ruby,或者Python等其他语言的话,就不会对Set感到陌生. 一个set就是一个不能包含重复项的有序列表.通常来说,你不需要像访问数组元素一样访问set中的元素,更常见的操作是检查一个元素是否被包含在某个set中.
ECMAScript 6中引入了Set类型
[1],你可以通过add()方法向一个set中添加元素,还可以使用
size()方法来查看一个set中的元素个数
:
var items = new Set(); items.add(5); items.add("5"); console.log(items.size()); // 2
Set类型在判断两个值是否是相同值的时候不会进行类型转换.所以,一个set可以同时包含数字5和字符串
"5"
(内部使用严格相等===
来比较).如果使用add()方法添加同一个值多次
,则除了第一次,后面的添加操作都会被忽略:
var items = new Set(); items.add(5); items.add("5"); items.add(5); // 已经添加过了,这个操作会被忽略
console.log(items.size()); // 2
译者注:这里作者说Set内部是使用严格相等
===
来比较两个值是否是同一个值的,这是不对的.实际上是使用了一个称之为SameValue算法的内部抽象操作来进行比较的.举两个反例:特殊值NaN:
var items = new Set(); items.add(NaN); //添加一个NaN元素 items.has(NaN) //如果说内部使用===来比较,由于NaN === NaN返回false,所以这里也应该返回false,但实际上返回了true.因为在SameValue算法中,NaN等于NaN特殊值+0和-0:
var items = new Set(); items.add(+0); items.has(-0); //false 因为在SameValue算法中正零和负零是两个不同的值 items.add(-0); items.size(); //2
你可以使用一个数组来初始化set,Set构造函数会自动过滤掉重复元素
:
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]); console.log(items.size()); // 5
在这个例子中,虽然数组中一共有四个5,但在生成的items中,数字5只剩下一个.这项功能可以更方便的把已有的代码或JSON结构转换成set.
可以使用has()方法来检测一个元素是否包含在某个set中
:
var items = new Set(); items.add(5); items.add("5"); console.log(items.has(5)); // true console.log(items.has(6)); // false
还可以使用delete()方法从一个set中删除某个元素
:
var items = new Set(); items.add(5); items.add("5"); console.log(items.has(5)); // true items.delete(5) console.log(items.has(5)); // false
迭代
虽然不能随机访问一个set中的元素,但可以使用ECMAScript 6中的for-of
语句[2]来遍历一个set.for-of语句可以遍历多种集合类型(包括数组和类数组结构)
:
var items = new Set([1, 2, 3, 4, 5]); for (let num of items) { console.log(num); }
上面的代码会把一个set中的所有元素按照添加时的顺序输出到控制台上.
例子
过去,如果你想跟踪一些唯一值,最常用的方法就是使用一个对象,把这些唯一值作为对象的属性名,属性值可以为任意真值.例如,在CSS Lint[3]中,有个功能是查找重复的CSS属性.目前就是用对象来实现的:
var properties = { "width": 1, "height": 1 }; if (properties[someName]) { // 进行一些操作 }
使用对象来完成这个任务时,必须给每个属性值赋一个真值,这样在进行if语句判断时,才能判断为真
(另外一个办法是使用in操作符,但很少有人这么用
).如果使用Set来完成这个任务,所有的操作都会变得很简单:
var properties = new Set(); properties.add("width"); properties.add("height"); if (properties.has(someName)) { // 进行一些操作 }
因为我们只关心一个属性以前是否用过,而不关心到底用了多少次,所以使用set更适合一点.
使用对象属性来进行这种操作的另一个缺点是:属性名总会被转换成字符串.所以不可能有一个属性名为数字5的对象,只能是字符串
"5"
.同样的道理,你不能使用这种方法来跟踪对象类型的值,因为对象值在作为一个属性名时同样会被转换成字符串.而Set可以包含任意一种类型的数据,且不用担心自动类型转换.
译者注:使用Set,对象属性,数组方法来进行数组去重的性能对比:http://jsperf.com/set-array-and-object-properties
浏览器支持
目前Firefox和Chrome都已经实现了Set类型
,只是在Chrome中,你必须先通过下面的操作激活ECMAScript 6的新特性:打开页面chrome://flags,勾选
“启用实验性 JavaScript”.需要注意的是,这两个浏览器的Set实现都不够完全.都没有实现Set的for-of迭代,
Chrome的实现还缺少size()方法
.
译者注:也许作者使用的是Firefox稳定版(15),目前Firefox aurora(17)版已经实现了Set和Map的遍历操作
译者注:为了兼容那些旧的浏览器,可以使用这个实现了ES6中多种集合类型(Set,Map,WeakMap,HashMap)的shim:http://benvie.github.com/harmony-collections/
综述
ECMAScript 6中的Set是一个非常有用的类型.它能让你很轻松的创建一个没有重复项的集合,而且不需要担心自动类型转换.你可以向一个set中添加或删除元素.如果需要的话,还可以使用ECMAScript 6中的for-of语句遍历一个set中的所有元素
.
由于ECMAScript 6还没有完成,在其他浏览器实现Set之前,规范和已有的实现都有可能会改变.目前,这些特性都被认为是实验性的API,所以不应该使用在实际项目中.本文,以及其他关于ECMAScript 6的文章,只能看作是对未来功能的预览.
参考
- Simple Maps and Sets (ES6 Wiki)
- for…of (MDN)
- CSS Lint
- Set (MDN)
译者注:上面的两个MDN链接已经替换为对应的中文页面,也是由我翻译的.