ES6系列---Set集合與Map集合

ES5中模擬Set與Map

長久以來,數組一直是JavaScript中惟一的集合類型。若是開發者們須要使用非數值型索引,就會用非數組對象建立所需的數據結構,而這就是Set集合與Map集合的早期實現。segmentfault

通常來講,Set集合常被用於檢查對象中是否存在某個鍵名,而Map集合常被用於獲取已存在的信息。數組

ES5模擬的Set集合

var set = Object.create(null);

set.foo = true;

// 檢查屬性是否存在
if(set.foo) {
    // 要執行的代碼
}

ES5中,開發者們常常用相似的方法檢查對象的某個屬性是否存在。數據結構

ES5模擬的Map集合

var map = Object.create(null);

map.foo = "bar";

// 獲取已存值
var value = map.foo;

console.log(value);   // "bar"

模擬這兩種集合對象的惟一區別是存儲的值不一樣。函數

ES5模擬解決方案的問題

通常狀況下,確實能夠用對象來模擬Set集合與Map集合,但若是觸碰到對象屬性的某些限制,狀況會變得複雜。請看這段代碼:this

var map = Object.create(null);
map[5] = "foo";

console.log(map["5"]);   // "foo"

因爲對象屬性名必須是字符串類型,示例中數值型的map[5],會被自動轉換成字符串map["5"]。若是你想分別用數字和字符串做爲對象屬性的鍵名,則內部的自動轉換機制會致使許多問題。調試

再看下面這個示例:code

var map = Object.create(null),
    key1 = {},
    key2 = {};
map[key1] = "foo";

console.log(map[key2]);  // "foo"

一樣,因爲對象屬性名必須是字符串,代碼中的map[key1]和map[key2]都將被轉換成map["[object object]"]。這種錯誤很難被發現。對象

對於Map集合來講,若是它的屬性值是假值,則在要求使用布爾值的狀況下(例如在if語句中)會被自動轉換爲false。強制轉換自己沒有問題,但若是考慮這個值的使用場景,就有可能發生問題。例如:索引

var map = Object.create(null);

map.count = 1;

// 本意是檢查"count"屬性是否存在,實際上檢查的是該值是否非零
if (map.count) {
    // 要執行的代碼
}

這個示例有些模棱兩可的地方。在if語句中,咱們是檢查map.count是否存在,仍是檢查值是否非零?因爲value的值是1,爲真值,if語句中的代碼將被執行。然而,若是map.count的值爲0或者不存在,if語句中的代碼將不被執行。ip

大型應用中,一旦發生此類問題將難以定位和調試,促使ES6加入SetMap集合兩種新特性。

ES6中的Set集合

ES6新增的Set是一種集合類型的數據結構,承載着有序不可重複的值。

Set集合的相關操做

  • new Set()構造函數:建立Set集合

  • add()方法:往集合添加元素

  • size屬性:集合長度

  • has()方法:判斷集合內是否包含某元素

  • delete()方法:從集合中刪除某元素

  • clear()方法:清空集合元素

  • forEach()方法:遍歷集合元素

相關操做示例:

let set = new Set();
set.add("5");
set.add(5);  // 不會強制類型轉換,數字5和字符串"5"能夠做爲兩個獨立元素存在
set.add(5); //  重複 - 本次調用直接被忽略

console.log(set.has(5));  // true
console.log(set.has("5"));  // true
console.log(set.has(6));  // false
console.log(set.size);  // 2

set.delete(5);

console.log(set.has(5));  // false
console.log(set.has("5"));  // true
console.log(set.size);  // 1

set.clear();

console.log(set.has("5"));  // false
console.log(set.size);  // 0

若是向Set集合添加多個對象,它們之間也是彼此獨立的:

let set = new Set();
    key1 = {},
    key2 = {};
    
set.add(key1);
set.add(key2);

console.log(set.size);  // 2

因爲key1key2不會被轉換成字符串,於是它們在Set集合中是兩個獨立元素。

用數組初始化Set集合

let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(set.size);  // 5

事實上,只要是可迭代對象(數組、Set集合、Map集合),均可以做爲Set構造函數的參數。構造函數經過迭代器從參數中提取值。

Set集合的forEach方法

Set集合的forEach方法與Map集合和數組中的forEach()方法相似,回調函數都接受3個參數:

  • Set集合當前遍歷元素的值

  • Set集合當前遍歷元素的鍵(Set集合鍵與值相同;數組是索引值)

  • Set集合自己

let set = new Set([1, 2]);

set.forEach(function(value, key, ownerSet) {
    console.log(key + " " + value);
    console.log(ownerSet === set);
});

以上示例輸出的內容:

1 1
true
2 2
true

若是須要在回調函數中使用this引用,則能夠將它做爲第二個參數傳入forEach()函數:

let set = new Set([1, 2]);

let processor = {
    output(value) {
        console.log(value);
    },
    process(dataSet) {
        dataSet.forEach(function(value)) {
            this.output(value);  // 從而正確調用到processor.output()方法
        }, this);
    }
};

processor.process(set);

固然,你可使用箭頭函數,這樣就無需再講this做爲第二個參數傳入了:

let set = new Set([1, 2]);

let processor = {
    output(value) {
        console.log(value);
    },
    process(dataSet) {
        dataSet.forEach(value => this.output(value));
    }
};

processor.process(set);

將Set集合轉換爲數組

儘管Set集合適合用來跟蹤多個值,並且又能夠經過forEach()方法操做集合中的每個元素,可是你不能像訪問數組元素那樣直接經過索引訪問集合中的元素。若有須要,先將Set集合轉換成一個數組。

展開運算符能夠很方便地將諸如Set集合的可迭代對象轉換爲數組:

let set = new Set([1, 2, 3, 3, 3, 4, 5]),
    array = [...set];
    
console.log(array);   // [1,2,3,4,5]

若是已經建立過一個數組,想要複製它並建立一個無重複元素的新數組,則上述這個方法十分管用,請看:

function eliminateDuplicates(items) {
    return [...new Set(items)];
}

let numbers = [1, 2, 3, 3, 3, 4, 5],
    noDuplicates = eliminateDuplicates(numbers);
    
console.log(noDuplicates);  // [1,2,3,4,5]

ES6中的Map集合

ES6中的Map類型是一種存儲着許多鍵值對的有序列表,其中的鍵名和對應的值支持全部的數據類型。

Map集合的相關操做

  • new Map()構造函數:建立Map集合

  • set()方法:往集合中添加新元素[鍵值對]

  • size屬性:集合元素個數

  • get()方法:從集合中獲取某個鍵對應的值

  • has()方法:判斷集合中是否存在某鍵的元素

  • delete()方法:從集合中刪除某鍵的元素

  • clear()方法:清空集合元素

  • forEach()方法:遍歷集合元素

相關操做示例:

let map = new Map();
map.set("name", "Nicholas");
map.set("age", 25);

console.log(map.size);  // 2

console.log(map.has("name"));  // true
console.log(map.get("name"));  // "Nicholas"
console.log(map.has("age"));  // true
console.log(map.get("age"));  // 25

map.delete("name");
console.log(map.has("name"));  // false
console.log(map.get("name"));  // undefined
console.log(map.size);  // 1

map.clear();
console.log(map.has("name"));  // false
console.log(map.get("name"));  // undefined
console.log(map.has("age"));  // false
console.log(map.get("age"));  // undefined
console.log(map.size);  // 0

用數組初始化Map集合

let map = new Map([["name", "Nicholas"], ["age", 25]]);

console.log(map.has("name"));  // true
console.log(map.get("name"));  // "Nicholas"
console.log(map.has("age"));  // true
console.log(map.get("age"));  // 25
console.log(map.size);  // 2

Map集合的forEach方法

Map集合的forEach方法與Set集合和數組中的forEach()方法相似,回調函數都接受3個參數:

  • Map集合當前遍歷元素的值

  • Map集合當前遍歷元素的鍵

  • Map集合自己

let map = new Map([["name", "Nicholas"], ["age", 25]]);

map.forEach(function(value, key, ownerMap) {
    console.log(key + " " + value);
    console.log(ownerMap === map);
});

會按照鍵值對插入Map集合順序遍歷,輸出內容:

name Nicholas
true
age 25
true
相關文章
相關標籤/搜索