[译]ECMAScript 6中的集合类型,第一部分:Set

原文: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的文章,只能看作是对未来功能的预览.

参考

  1. Simple Maps and Sets (ES6 Wiki)
  2. for…of (MDN)
  3. CSS Lint
  4. Set (MDN)

译者注:上面的两个MDN链接已经替换为对应的中文页面,也是由我翻译的.

原文地址:https://www.cnblogs.com/ziyunfei/p/2703484.html