原文: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鏈接已經替換為對應的中文頁面,也是由我翻譯的.