ECMAScript6(11):Set 與 Map

Set

Set 是一種集合結構,特徵和數學中的一致,具備如下特徵:數組

  • 同一個集合中不能有相同元素
  • set 能夠存放不一樣類型的數據

但使用過程當中請注意如下幾點:數據結構

  • 存入 set 的數據不會進行類型轉換,即'5'和 5 是不同的
  • 內部採用嚴格相等比較元素,但-0等於+0,NaN也等於NaN

定義聚合和定義其餘數據結構同樣,其構造函數接受一個數組,集合或類數組對象初始化:函數

var set1 = new Set();
var set2 = new Set([1,2,3,3,4,4,5]);
console.log(set1);   //Set(0) {}
console.log(set2);   //Set(5) {1, 2, 3, 4, 5}

Set結構具備如下屬性和方法,因爲和數組方法基本一致,不細細列舉this

  • size屬性: 當前集合的元素數量,至關於數組熟悉的length
  • constructor屬性: Set()
  • add()方法: 至關於數組的push()方法,但只能接受一個參數
  • delete()方法: 刪除集合中的一個值
  • has()方法: 判斷數組的中是否含有某個值
  • clear()方法: 清空當前數組
  • keys()方法: 返回鍵名的遍歷器,和數組keys()方法同樣
  • values()方法: 返回值的遍歷器,和數組values()方法同樣
  • entires()方法: 返回鍵值對的遍歷器,和數組entires()方法同樣
  • forEach()方法: 使用回調函數遍歷集合成員,和數組forEach()方法同樣
  • map()方法: 至關於數組的map()方法
  • filter()方法: 至關於數組的filter()方法

Set的默認遍歷器遍歷的是值:prototype

Set.prototype[Symbol.iterator] === Set.prototype.values;   //true

集合運算:code

  • 並集
var a = new Set([1,2,3]);
var b = new Set([2,4,5]);
var union = new Set([...a, ...b]);    //[1,2,3,4,5]
  • 交集
var a = new Set([1,2,3]);
var b = new Set([2,4,5]);
var intersect = new Set([...a].filter(item => b.has(item)));  //[2]
  • 差集
var a = new Set([1,2,3]);
var b = new Set([2,4,5]);
var diffsect = new Set([...a].filter(item => !b.has(item)));  //[1,3]

WeakSet

WeakSet 和 Set相似,可是具備如下區別:對象

  • WeakSet 的元素只能是對象,不能是別的類型
  • WeakSet 的元素沒法被引用,其元素不具備別的引用時,GC 會馬上釋放對象的內存資源,所以 WeakSet 不能被遍歷。

定義WeakSet和定義其餘數據結構同樣,其構造函數接受一個數組,集合或類數組對象初始化:three

var set1 = new WeakSet();
var set2 = new WeakSet([[1,2],[3,3],[4,4,5]]);
console.log(set1);   //WeakSet(0) {}
console.log(set2);   //WeakSet {(2) [1, 2], (2) [3, 3], (3) [4, 4, 5]}

WeakSet 沒有 size 屬性,有以下3個方法:內存

  • add()方法: 至關於數組的push()方法,但只能接受一個參數
  • delete()方法: 刪除集合中的一個值
  • has()方法: 判斷數組的中是否含有某個值

WeakSet 不能遍歷,它的做用是臨時存儲DOM節點,這樣沒必要擔憂內存泄漏:資源

//例
const foos = new WeakSet();
class Foo{
  constructor(){
    foos.add(this);
  }
  method(){
    if(!foos.has(this)){
      throw new TypeError(`"foo.prototype.method" is only called by object of Foo`);
    }
    console.log(`"Foo.prototype.method" has been called`);
  }
}
var obj = new Foo();
obj.method();          //Foo.prototype.method" has been called
obj.method.call({});   //typeError: "foo.prototype.method" is only called by object of Foo

Map

js 中的對象是鍵值對的集合,可是鍵只能是字符串其實並不方便。Map結構本質和對象同樣,只是鍵名可使用各類類型的值。若是這麼理解,那麼Map就是一種值-值對而不是鍵-值對,這一點相似hash結構:

var o = {name: "Bob"};
var map = new Map();
map.set(o, "hello");
console.log(map.get(o));   //"hello"

構造 Map 結構的構造函數接受數組,對象等類型做爲構造函數的參數:

var map = new Map([["name","Bob"], ["age", 12]]);
map.get("name");    //"Bob"
map.get("age");     //12

map具備以下屬性和方法:

  • size 屬性: 返回 map 中元素的數量,相似數組的 length
  • set(key, value)方法: 向map中添加值-值對
  • get(key)方法: 讀取map中的值
  • delete(key, value)方法: 刪除map中的值-值對
  • has(key)方法: 判斷某個鍵名是否存在
  • clear()方法: 清空當前 map 中因此數據
  • keys()方法: 返回鍵名的遍歷器,和數組keys()方法相似,用於 for...of 循環
  • values()方法: 返回值的遍歷器,和數組values()方法相似,用於 for...of 循環
  • entires()方法: 返回值-值對的遍歷器,和數組entires()方法相似,用於 for...of 循環
  • forEach()方法: 使用回調函數遍歷集合成員,和數組forEach()方法相似
var o = {name: "Bob"};
var map = new Map();
map.set(o, "hello");
console.log(map.get(o));   //"hello"
map.set(o, "world");       //重複定義,覆蓋以前的定義
console.log(map.get(o));   //"world"
console.log(map.get({name: "Bob"}));   //undefined, 不一樣的對象引用不認爲是同一個對象
map.delete(o);             //刪除對象屬性 o
console.log(map.get(o));   //undefined

從上方的例子不難發現,不一樣的對象屬性對於 map 來講就是不一樣的,無論內部的內容是否一致。這和對象的 === 比較是同樣的道理,帶來的好處是咱們不用擔憂和會 map 原有屬性重名,而直接向 map 添加對象屬性便可。

注意 undefined,NaN和 null 也能夠做爲 map 的鍵名

var map = new Map();
map.set(undefined, 1);
map.set(null, 2);
map.set(NaN, 3);
console.log(map.get(undefined));        //1
console.log(map.get(null));        //2
console.log(map.get(NaN));        //3

但使用過程當中請注意如下幾點:

  • 存入 map 的數據不會進行類型轉換,即'5'和 5 是不同的, {} 和 {}也是不同的。
  • 內部採用嚴格相等比較元素,但-0等於+0,NaN也等於NaN。

map 的默認遍歷器是 entries()

Map.prototype[Symbol.iterator] === Map.prototype.entries;   //true

另外這裏須要格外強調的是:

  • Set中的 has 方法是判斷鍵值是否存在的,如 Set.prototype.has(value), WeakSet.prototype.has(value)
  • Map中的 has 方法是判斷鍵名是否存在的,如 Map.prototype.has(key), WeakMap.prototype.has(key), Reflect.has(target, propertyKey)

Map解構轉換技巧:

  • Map 轉 Array
var map = new Map([[1,'one'], [2, 'two'], [3, 'three']]);
var keyArr = [...map.keys()];          //[1,2,3]
var valueArr = [...map.values()];      //['one','two','three']
var entriesArr = [...map.entries()];   //[[1,'one'], [2, 'two'], [3, 'three']]
var arr = [...map];                    //[[1,'one'], [2, 'two'], [3, 'three']]
  • Map 轉 Object(爲防止沒必要要的錯誤,直接丟棄不是字符串爲鍵的屬性)
function map2arr(map){
  var o = {};
  for(let [key, value] of map.entries()){
    if(typeof key === 'string'){
      o[key] = value;
    }
  }
  return o;
}
var map = new Map([[1,'one'], [2, 'two'], ['three', 3], ['four', 4]]);
map2arr(map);    //Object {three: 3, four: 4}
  • Map 轉 JSON
var map = new Map([[1,'one'], [2, 'two'], ['three', 3], ['four', 4]]);
JSON.stringify([...map]);    //"[[1,"one"],[2,"two"],["three",3],["four",4]]"

WeakMap

WeakMap 和 map 相似,不過它只接受對象做爲鍵名,null除外。試想,若是對象 o 是一個 map的屬性,若是該對象被釋放了,那這個 map 屬性會致使內存溢出。解決這個問題就是使用 WeakMap:

var o = {name: "Bob"};
var map = new WeakMap();
map.set(o, 12);
console.log(map.get(o));    //12
o = null;
console.log(map.get(o));    //undefined

WeakMap的對象屬性名,不計入 GC,因此當對象不存在的時候,這個 weakmap 中相應的鍵值對就被刪除了。值得一提的是,代碼對於 map 能夠獲得同樣的輸出。那是由於最後一行至關於console.log(map.get(null)), 咱們沒有定義 null 對應的值,因此獲得 undefined, 其實內存泄露的隱患依然存在:

var o = {name: "Bob"};
var map = new Map();
map.set(o, 12);
console.log(map.get(o));    //12
o = null;
console.log(map.size);      //1

WeakMap 和 WeakSet 相似,因爲不計入 GC 回收機制,因此不支持遍歷操做,也不支持被清空,因此 WeakMap 只有如下 4 個方法,沒有 size 屬性:

  • set(key, value)方法: 向weakMap中添加值-值對
  • get(key)方法: 讀取map中的值
  • delete(key, value)方法: 刪除weakMap中的值-值對
  • has(key)方法: 判斷某個鍵名是否存在
相關文章
相關標籤/搜索