長久以來,數組一直是JavaScript
中惟一的集合類型。若是開發者們須要使用非數值型索引,就會用非數組對象建立所需的數據結構,而這就是Set
集合與Map
集合的早期實現。segmentfault
通常來講,Set
集合常被用於檢查對象中是否存在某個鍵名,而Map
集合常被用於獲取已存在的信息。數組
var set = Object.create(null); set.foo = true; // 檢查屬性是否存在 if(set.foo) { // 要執行的代碼 }
ES5
中,開發者們常常用相似的方法檢查對象的某個屬性是否存在。數據結構
var map = Object.create(null); map.foo = "bar"; // 獲取已存值 var value = map.foo; console.log(value); // "bar"
模擬這兩種集合對象的惟一區別是存儲的值不一樣。函數
通常狀況下,確實能夠用對象來模擬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
加入Set
與Map
集合兩種新特性。
ES6
新增的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
因爲key1
和key2
不會被轉換成字符串,於是它們在Set集合中是兩個獨立元素。
let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]); console.log(set.size); // 5
事實上,只要是可迭代對象(數組、Set集合、Map集合),均可以做爲Set構造函數的參數。構造函數經過迭代器從參數中提取值。
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
集合適合用來跟蹤多個值,並且又能夠經過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
類型是一種存儲着許多鍵值對的有序列表,其中的鍵名和對應的值支持全部的數據類型。
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
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方法與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