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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM