es6入門7--Set Map數據結構

本文做爲ES6入門第十三章的學習整理筆記,可能會包含少部分我的的理解推測,若想閱讀更詳細的介紹,還請閱讀原文ES6入門es6

1、set數據結構數組

1.set不接受重複值數據結構

ES6新增了Set構造函數用於建立set數據結構,這種結構相似於數組,但有很大的一個區別就是,set數據結構不接受重複值,每一個值都是惟一的。dom

咱們能夠經過Set構造函數快速建立一個set數據結構,順便打印看看究竟長什麼樣:函數

let s = new Set();
console.dir(s);

那麼能夠看到,set實例具備一個size屬性,由於咱們還未給此結構添加值,因此是0,相似於數組的length屬性。學習

set實例還有不少方法,例如add添加,clear清除,還有在數組拓展中已經介紹過的keys,values等比較熟悉的方法,這些後面具體再說。spa

咱們嘗試在new命令時直接初始化值:prototype

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

能夠看到,儘管我添加了兩個數字1,最終的set實例結構中只有一個不重複的1,這是由於set不接受重複的值,自帶去重效果。code

你可能看過如下數組去重的快捷方法,正式利用的set的這一特色:對象

// 數組去重
[...new Set([1, 1, 2, 3, 4, 4])];
Array.from(new Set([1, 1, 2, 3, 4, 4]));

2.set實例的增刪改查方法

add方法:添加某個值,返回添加值後的set解構,相似數組的push,後添加的元素在set解構後面。

let s = new Set();
s.add(1).add(2);

has方法:查找set解構是否包含某值,返回一個布爾值。

s.has(1); //true
s.has(3); //false

delete方法:刪除某個值,返回一個布爾值對應是否刪除成功。

s.delete(1);//true
s.delete(1);//false

clear方法:清除整個set解構,無返回值。

s.clear();

 3.set的遍歷方法

keys方法:遍歷元素的鍵名

values方法:遍歷元素的鍵值

entries方法:遍歷元素的鍵值對

forEach方法:用的賊多,回調函數遍歷每一個元素

在數組拓展這一章節中也有介紹這三個方法,這裏就簡單說下;三個方法都是結合for...of循環使用,分別遍歷元素的key,value與key/value組合

let s = new Set([{a:1}, {b:2}, {c:3}]);
for (let item of s.keys()) {
    console.log(item);// {a:1}, {b:2}, {c:3}
};
for (let item of s.values()) {
    console.log(item);// {a:1}, {b:2}, {c:3}
};
for (let item of s.entries()) {
    console.log(item);// [{a:1},{a:1}],[{b:2},{b:2}],[{c:3},{c:3}]
};

經過上述代碼中的輸出能夠了解到,keys方法與values方法執行徹底相同,這是由於set解構沒有key名致使,key名與value相同;而entries方法每次返回的是一個包含了key與value的數組。

當咱們想遍歷出set解構的每一個元素理論上使用values方法,有趣的是set解構的默認遍歷器恰好與values相等,因此咱們甚至能省略掉values方法直接遍歷解構中的每一個元素。

let s = new Set([1, 2, 3]);
Set.prototype[Symbol.iterator] === Set.prototype.values; //true
//省略values方法
for(let item of s){
    console.log(item);//1 2 3
};

 與數組中使用這三個方法的區別在於,數組中的keys遍歷的是元素的下標,values相同,entries是下標和元素組成鍵值對,且不是數組。

當咱們使用forEach遍歷set結構數據時,回調參數三個參數的前兩個徹底相同,這也是由於key名與key值相同的緣故,這點須要注意。

let s = new Set([1, 2, 3]);
s.forEach((val,key) => console.log(val,key))//1 1,2 2,3 3

4.set解構的做用

a.數組去重,主要利用了set不接受重複值作參數的特色。

b.set結構實現並集,簡單點說,就是把兩個set重複項去掉,原理仍是利用set不接受重複項

let a = new Set([1, 2, 3]);
let b = new Set([2, 3, 4]);
let s1 = new Set([...a, ...b]); //set {1,2,3,4}

c.set結構實現交集,原理是利用了set實例的has方法

let s2 = new Set([...a].filter(x => b.has(x)))//set {2,3}

d.set結構實現差集,同理利用了has方法

let s3 = new Set([...a].filter(x => !b.has(x)))//set {1}

你的直覺是否是這裏應該是{1,4},這裏的差集實際上是a裏面有且b裏面沒有的元素,而不是ab互相沒有。

2、WeakSet結構

WeakSet數據結構與Set相似,也不接受重複的值,但也有三點不一樣,一是WeakSet解構的成員只能是對象,二是WeakSet中的對象都是弱引用,三是WeakSet沒法遍歷

1.WeakSet成員只能是對象

let s = new WeakSet();

s.add([{a:1},{b:2}]);
console.dir(s);

s.add(1);//報錯 Invalid value used in weak set

建立WeakSet 結構可經過new命令完成,WeakSet 接受任何含有Iterable接口的對象做爲參數。能夠看到當咱們add非對象元素,該操做報錯,可是add添加對象沒問題。

那麼咱們看這段代碼,爲何報錯了:

let s = new WeakSet([1,2,3]);

我在前面你說了,WeakSet的每一個成員必須是對象,前面咱們使用的是add方法,每次添加都是一個成員,這是直接使用new初始化,雖然傳遞的參數是數組,但本質上等同於:

let s = new WeakSet();
s.add(1).add(2).add(3);

因此咱們須要保證數組中的每一個元素也是對象,這樣就不會報錯了:

let s = new WeakSet([{a:1},{b:2}]);

其次能夠看到WeakSet方法並很少,add,has,delete三個,用法和set相同,這裏就不重複介紹了。

2.WeakSet結構成員均爲弱引用

咱們都知道,當一個對象不被任何地方引用,垃圾回收機制就會釋放掉這個對象所佔用內存。咱們在前面說WeakSet的成員都是對象,可是垃圾回收機制不考慮WeakSet的引用。

說直白點,如今對象a被A和WeakSet同時引用,A再也不引用了垃圾回收機制就直接釋放了,徹底無論WeakSet還在引用它。

也正是由於WeakSet成員是弱引用的緣由,咱們沒法保證何時成員就被釋放了,因此WeakSet沒有size屬性,也不可遍歷。

3、map數據結構

1.基本用法與增刪改查方法

傳統意義上的對象都是鍵值對組成的集合,鍵爲字符串,值爲一個對象,咱們是沒法使用對象做爲鍵的。

但Map打破了這個規則,咱們能夠經過Map建立鍵值都是對象的數據結構,這樣鍵再也不是做爲保存值的存在,在遍歷時,鍵值均可以是有效的對象。

let m = new Map();
console.dir(m);

從上圖中,能夠看到百分之80的方法與Set數據結構徹底相同,只是多了一個set方法和get方法。

set(key,value)方法:按照key/value添加成員,返回Map結構,支持鏈式寫法;若是key已存在,則覆蓋

get(key)方法:按照key查找返回對應的value,若是未找到,返回undefined。

has(key)方法:查找是否包含某個key,返回一個布爾值。

delete(key)方法:刪除對應的key,返回一個布爾值,表示是否成功刪除。

clear()方法:清空整個Map數據結構。

let m = new Map();
let o = {name:'echo'};
m.set(o,{age:26});
m.get(o);//{age:26}
m.has(o);//true
m.delete(o);//true
m.has(o);//false

那麼在上述代碼中,咱們爲map數據結構添加了一個key爲{name:'echo'}值爲{age:26}的成員。

同時咱們能夠經過get指定的key訪問到對應的value,delete仍是同樣返回是否刪除成功,has依舊是判斷該數據結構是否含有此成員。

添加成員固然不要求經過set,在new命令執行時,咱們能夠以一個數組的形式傳遞須要添加的成員。

let m = new Map([
    ['name', '聽風是風'],
    ['age', 26]
]);
m.has('name') //true
m.get('name') //聽風是風
m.has('age') //true
m.get('age') //26

其實初次看到這我是有點懵逼的,爲何我一個數組成員的兩個元素,成了Map數據結構中一個成員的key與value。其實這個不難理解,它等同於如下的執行:

let arr = [
    ['name', '聽風是風'],
    ['age', 26]
];
let m = new Map();
arr.forEach(([key, value]) => m.set(key, value))

數組每一個元素又是一個雙元素數組,前者做爲map的key,後者做爲map的value

須要注意的是,map數據結構一樣不接受重複的值做爲成員,這裏的重複是指key名相同,若是相同,後者會覆蓋前者

const m = new Map([
    ['name', 1],
    ['name', 2]
]);
console.log(m);//key:name value:2

除此以外,當咱們map的key是對象時,須要注意對象引用的問題:

let o = {name:1};
let m = new Map();
m.set(o,2)
console.log(m.get(o));//2
m.set({name:1},2)
console.log(m.get({name:1}));//undefined

在上述代碼中,若是咱們直接將{name:1}做爲key用於存值,在set執行時,沒法拿到對應的value,這是由於對象儘管寫法相同,但仍然是徹底不一樣的兩個東西;

因此在須要將對象作key時,請將此對象賦予一個變量,利用此變量做爲key進行存儲,在讀取時再次讀取這個變量,就能夠避免這個問題了。

其實說到這裏,關於map的key,實際上是跟內存地址相關。若是key是一個簡單數據類型,那麼只要兩個key徹底相等,就視爲一個key,且後者覆蓋前者,若是不相等,則反之。

若是key是一個對象,想正確的存取,請將對象賦予給一個變量再作set操做。不然會由於引用地址問題沒法訪問到你已經添加的key。

2.Map數據結構的遍歷方法

keys()方法:遍歷並返回鍵名

let m = new Map([
    ['name', '聽風是風'],
    ['age', 26]
]);
for(let key of m.keys()){
    console.log(key);//name age
};

values()方法:遍歷並返回鍵值

for(let value of m.values()){
    console.log(value);//聽風是風 26
};

entries()方法:遍歷返回全部成員,注意,我沒說這裏是返回鍵值對

for(let item of m.entries()){
    console.log(item);
};

如上圖,返回兩個數組,每一個數組分別包含了key和value,因此若是咱們想直接訪問key,value,應該這麼寫:

for(let [key,value] of m.entries()){
    console.log(key,value);//name 聽風是風,age 26
};

還記得在介紹Set數據結構是,咱們說Set的默認遍歷器接口等於values方法,因此咱們能夠簡寫遍歷,比較好運的是,Map數據結構的默認遍歷器接口等於entries方法,因此咱們還能夠繼續簡寫:

m[Symbol.iterator] === m.entries; //true
for (let [key, value] of m) {
    console.log(key, value);//name 聽風是風,age 26
};

forEach方法,經過回調參數也能夠方便的訪問到Map結構的key與value

m.forEach((value, key, m) => console.log(value, key));//聽風是風 name,26 "age"

4、WeakMap數據結構

 WeakMap與Map結構相似,但也有兩點不一樣,一是WeakMap成員的key只接受對象

let m = new WeakMap();
m.set('name',1);//報錯

二是WeakMap的鍵名所引用對象爲弱引用,也就是不計入垃圾回收機制,這點與WeakSet一致。

let m = new WeakMap();
let ele = document.querySelector("#div");
m.set(ele, '這是一個div元素');
m.get(ele); //這是一個div元素

在上述代碼中,咱們先是將獲取的dom存在了ele變量中,此時對於div dom的引用次數是1次。

而後咱們又將ele做爲key,爲這個ele添加了一些說明,照理說,div dom此時又被WeakMap結構引用,因此div引用次數是2次。

但因爲WeakMap的key名對象是弱引用,因此這裏div一共的引用此事仍是1次。當咱們讓ele再也不引用div元素時,垃圾回收機制不會考慮WeakMap對於div的引用,而是直接釋放,這點其實與WeakSet是保持一致的。

強調一點的是,WeakMap弱引用的是key,而不是value,這裏有個例子:

let m = new WeakMap();
let key = {};
let value = {a: 1};
m.set(key, value);
value = null;
m.get(key) //{a:1}

即使咱們將WeakMap中value所引用的對象釋放,其實垃圾回收機制仍是將WeakMap的引用計爲1次,因此還能正常讀取到。

由於key是弱引用的緣故,因此與WeakSet同樣,不存在遍歷方法。

WeakMap結構最大的一個用處就是用於保存dom,這樣dom元素被刪除也不會形成內存泄漏問題:

let ele = document.getElementById('logo');
let fn = function () {
    console.log(1)
};
let m = new WeakMap();
//將dom元素與須要執行的函數做爲WeakMap結構的key與value
m.set(ele, fn);
//爲dom元素增長監聽
ele.addEventListener('click', function () {
    //執行監聽函數
    m.get(ele)();
}, false);

關於WeakMap這裏就很少作介紹了,至少我目前開發基本使用不到.....

不僅是是WeakMap,Set與Map的使用機率基本很低,這裏就純作一個整理了,往後萬一用到,或者說使用逐漸普及,也方便查找。

那麼就寫到這裏了。

相關文章
相關標籤/搜索