深入理解ES6--7.Set和Map

原創文章&經驗總結&從校招到A廠一路陽光一路滄桑

詳情請戳www.coderccc.com

> 主要知識點:Set的基本操作,Weak Set,Map的基本操作,Weak Map

Set和Map知識點

1.ES6中的Set

ES6中提供了Set數據容器,這是一個能夠存儲無重複值的有序列表。

創建Set

通過new Set()可以創建Set,然後通過add方法能夠向Set中添加數據項:

//Set
let set= new Set();
set.add(1);
set.add('1');
console.log(set.size);//2		

Set內部使用Object.is()方法來判斷兩個數據項是否相等,唯一不同的是+0和-0在Set中被判斷爲是相等的。

同時可以使用數組來構造Set,或者說具有迭代器的對象都可以用來構造Set,並且Set構造器會確保不會存在重複的數據項:

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

檢查某個值是否存在於Set中

可以使用has()方法來判斷某個值是否存在於Set中:

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

刪除值

使用delete()方法從Set中刪除某個值,或者使用clear()方法從Set中刪除所有值:

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

set.delete(1);

console.log(set.has(1)); //false
console.log(set.size); //2

forEach()方法

可以使用forEach方法來遍歷Set中的數據項,該方法傳入一個回調函數callback,還可以傳入一個this,用於回調函數之中:

回調函數callback中有三個參數:

  1. 元素值;

  2. 元素索引;

  3. 將要遍歷的對象;

    let set = new Set([1,2,3,3,3,3]);
    set.forEach(function (value,key,ownerSet) {
    console.log(value);
    console.log(key);
    })

Set中的value和key是相同的,這是爲了讓Set的forEach方法和數組以及Map的forEach方法保持一致,都具有三個參數。

在forEach方法中傳入this,給回調函數使用:

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

	print(value){
		console.log(value);
	},

	iterate(set=[]){
		set.forEach(function(value,key,ownerSet){
			this.print(value);
			this.print(key);
		},this);
	}

}

operation.iterate(set);

輸出:1 1 2 2 3 3

如果回調函數使用箭頭函數的話,就可以省略this的入參,這是因爲箭頭函數會通過作用域鏈找到當前this對象,將上面的示例代碼使用箭頭函數來寫:

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

	print(value){
		console.log(value);
	},

	iterate(set=[]){
		set.forEach((value,key)=>{
			this.print(value);
			this.print(key);
		})
	}

}

operation.iterate(set);

將Set轉換成數組

將數組轉換成Set十分容易,可以將數組傳入Set構造器即可;而將Set轉換成數組,需要使用擴展運算符。擴展運算符能將數組中的數據項切分開,作爲獨立項傳入到函數,如果將擴展運算符用於可迭代對象的話,就可以將可迭代對象轉換成數組:

let [...arr]=set;
console.log(arr); // [1,2,3]

Weak Set

Set在存放對象時,實際上是存放的是對象的引用,即Set也被稱之爲Strong Set。如果所存儲的對象被置爲null,但是Set實例仍然存在的話,對象依然無法被垃圾回收器回收,從而無法釋放內存:

let set = new Set();
let key={};
let key2 = {};
set.add(key);
set.add(key2);
console.log(set.size); //2

key=null;
console.log(set.size); //2

可以看出就算對象key置爲null,但是由於是強引用的方式,Set實例還存在,對象key依然不會被回收。

如果想讓對象key正常釋放的話,可以使用Weak Set,此時,存放的是對象的弱引用,當對象只被Set弱引用的話,並不會阻止對象實例被回收。Weka Set同Set的用法幾乎一致。可以使用add()方法增加數據項,使用has()方法檢查Weak Set中是否包含某項,以及使用delete()方法刪除某一項。

let set = new WeakSet();
let key = {};	
set.add(key);
console.log(set.has(key)); //true
set.delete(key);
console.log(set.has(key)); //false

但需要注意的是:Weak Set構造器不接受基本類型數據,只接受對象。同樣的可以使用可迭代的對象如數組,來作爲構造器參數,來創建Weak Set。

Weak Set和Set之間的差異

對於Weak Set和Set之間的重要差異:

  1. 對於Weak Set實例,若調用了add()方法時傳入了非對象的參數,則會拋出錯誤。如果在has()或者delete()方法中傳入了非對象的參數則會返回false;
  2. Weak Set不可迭代,因此不能用於for-of循環;
  3. Weak Set 無法暴露出任何迭代器(例如 keys() 與 values() 方法) ,因此沒有任何編程手段可用於判斷 Weak Set 的內容;
  4. Weak Set沒有forEach()方法;
  5. Weak Set沒有size屬性;

3. ES6中的Map

ES6中提供了Map數據結構,能夠存放鍵值對,其中,鍵的去重是通過Object.is()方法進行比較,鍵的數據類型可以是基本類型數據也可以是對象,而值也可以是任意類型數據。

對Map的操作

  1. 使用set()方法可以給Map添加鍵值對

    let map = new Map();
    map.set(‘title’,‘hello world’);
    map.set(‘year’,‘2018’);

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

通過set()方法往Map中增加了兩個鍵值對後,可以看到Map的大小就爲2;

  1. 通過get()方法可以從Map中提取值

    let map = new Map();
    map.set(‘title’,‘hello world’);
    map.set(‘year’,‘2018’);

    console.log(map.get(‘title’)); // hello world

  2. has(),delete()以及clear()方法

爲了和Set的操作保持一致,Map中同樣有has()方法,用來檢查某個數據項是否存在於Map中,使用delete方法可以從Map中刪除一個數據項,使用clear方法可以刪除Map中所有的數據項

let map = new Map();
map.set('title','hello world');
map.set('year','2018');

console.log(map.has('year')); //true
map.delete('title');
console.log(map.has('title')); //false
map.clear();
console.log(map.size); //0

Map的初始化

與Set的初始化一樣,Map也可以用數組來初始化Map,該數組中的每一個數據項也是數組,數組的第一個數據項代表鍵值對的鍵,第二個數據項是鍵值對的值:

//使用數組來創建Map
let map = new Map([['title','hello world'],['year','2018']]);
console.log(map.has('title')); //true
console.log(map.has('year')); //true
console.log(map.size); //2

Map的forEach方法

與Set一樣,Map也擁有forEach方法,該方法也接收一個回調函數,該回調函數有三個參數:

  1. 鍵值對的鍵;

  2. 鍵值對的值;

  3. 當前Map對象引用;

     let map = new Map([['title','hello world'],['year','2018']]);
     map.forEach((value,key,ownerMap)=>{
     	console.log(value);
     	console.log(key);
     });
     
     hello world
     title
     2018
     year
    

與Set的forEach一樣,可以在回調函數中傳入this引用

Weak Map

Weak Map對Map而言,就像是Weak Set相對於Set一樣:Weak Map(或者Weak Set)都是存儲對象弱引用的方式,在Weak Map(或者Weak Set)中,所有的鍵都必須是對象(嘗試使用非對象的鍵會拋出錯誤),而且這些對象都是弱引用,不會干擾到垃圾回收。當Weak Map中的鍵在Weak Map之外不存在引用時,該鍵值對會被移除。

Weak Map的操作

  1. Weak Map的初始化

Weak Map的鍵必須是對象,值可以是任意類型,初始化同Map一樣,也可是使用數組來創建一個 Weak Map :

	//使用數組來創建一個Weak Map
	let key = {};
	let key2 = {};
	let map = new WeakMap([[key,'hello'],[key2,'world']]);
	console.log(map.get(key)); //hello
	console.log(map.get(key2)); //world
  1. has方法以及delete方法

與Map一樣,可以使用has()方法來檢查Weak Map中是否存在某一個鍵值對,使用delete()方法可以刪除一個鍵值對。clear() 方法不存在,這是因爲沒必要對鍵進行枚舉,並且枚舉 Weak Map 也是不可能的,這與 Weak Set 相同:

let key = {};
let key2 = {};
let map = new WeakMap([[key,'hello'],[key2,'world']]);

map.delete(key);
console.log(map.has(key)); //false

Weak Map 的用法與侷限性

當決定是要使用 Weak Map 還是使用正規 Map 時,首要考慮因素在於你是否只想使用對象類型的鍵。如果你打算這麼做,那麼最好的選擇就是 Weak Map 。因爲它能確保額外數據在不再可用後被銷燬,從而能優化內存使用並規避內存泄漏。

要記住 Weak Map 只爲它們的內容提供了很小的可見度,因此你不能使用 forEach() 方法、size 屬性或 clear() 方法來管理其中的項。如果你確實需要一些檢測功能,那麼正規 Map會是更好的選擇,只是一定要確保留意內存的使用。

4. 總結

  1. Set 是無重複值的有序列表。根據 Object.is()方法來判斷其中的值不相等,以保證無重複。 Set 會自動移除重複的值,因此你可以使用它來過濾數組中的重複值並返回結果。 Set並不是數組的子類型,所以你無法隨機訪問其中的值。但你可以使用has() 方法來判斷某個值是否存在於 Set 中,或通過 size 屬性來查看其中有多少個值。 Set 類型還擁有forEach()方法,用於處理每個值。

  2. Weak Set 是隻能包含對象的特殊 Set 。其中的對象使用弱引用來存儲,意味着當 Weak Set中的項是某個對象的僅存引用時,它不會屏蔽垃圾回收。由於內存管理的複雜性, Weak Set的內容不能被檢查,因此最好將 Weak Set 僅用於追蹤需要被歸組在一起的對象。

  3. Map 是有序的鍵值對,其中的鍵允許是任何類型。與 Set 相似,通過調用 Object.is()方法來判斷重複的鍵,這意味着能將數值 5 與字符串 “5” 作爲兩個相對獨立的鍵。使用set() 方法能將任何類型的值關聯到某個鍵上,並且該值此後能用 get() 方法提取出來。Map 也擁有一個 size 屬性與一個 forEach() 方法,讓項目訪問更容易。

  4. Weak Map 是隻能包含對象類型的鍵的特殊 Map 。與 Weak Set 相似,鍵的對象引用是弱引用,因此當它是某個對象的僅存引用時,也不會屏蔽垃圾回收。當鍵被回收之後,所關聯的值也同時從 Weak Map 中被移除。

發佈了40 篇原創文章 · 獲贊 38 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章