主要知識點:Set的基本操作,Weak Set,Map的基本操作,Weak 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中有三個引數:
-
元素值;
-
元素索引;
-
將要遍歷的物件;
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之間的重要差異:
- 對於Weak Set例項,若呼叫了add()方法時傳入了非物件的引數,則會丟擲錯誤。如果在has()或者delete()方法中傳入了非物件的引數則會返回false;
- Weak Set不可迭代,因此不能用於for-of迴圈;
- Weak Set 無法暴露出任何迭代器(例如 keys() 與 values() 方法) ,因此沒有任何程式設計手段可用於判斷 Weak Set 的內容;
- Weak Set沒有forEach()方法;
- Weak Set沒有size屬性;
3. ES6中的Map
ES6中提供了Map資料結構,能夠存放鍵值對,其中,鍵的去重是通過Object.is()方法進行比較,鍵的資料型別可以是基本型別資料也可以是物件,而值也可以是任意型別資料。
對Map的操作
-
使用
set()
方法可以給Map新增鍵值對let map = new Map(); map.set('title','hello world'); map.set('year','2018'); console.log(map.size); //2 複製程式碼
通過set()
方法往Map中增加了兩個鍵值對後,可以看到Map的大小就為2;
-
通過get()方法可以從Map中提取值
let map = new Map(); map.set('title','hello world'); map.set('year','2018'); console.log(map.get('title')); // hello world 複製程式碼
-
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
方法,該方法也接收一個回撥函式,該回撥函式有三個引數:
-
鍵值對的鍵;
-
鍵值對的值;
-
當前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的操作
- 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
複製程式碼
- 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. 總結
-
Set 是無重複值的有序列表。根據
Object.is()
方法來判斷其中的值不相等,以保證無重複。 Set 會自動移除重複的值,因此你可以使用它來過濾陣列中的重複值並返回結果。 Set並不是陣列的子型別,所以你無法隨機訪問其中的值。但你可以使用has()
方法來判斷某個值是否存在於 Set 中,或通過size
屬性來檢視其中有多少個值。 Set 型別還擁有forEach()
方法,用於處理每個值。 -
Weak Set 是隻能包含物件的特殊 Set 。其中的物件使用弱引用來儲存,意味著當 Weak Set中的項是某個物件的僅存引用時,它不會遮蔽垃圾回收。由於記憶體管理的複雜性, Weak Set的內容不能被檢查,因此最好將 Weak Set 僅用於追蹤需要被歸組在一起的物件。
-
Map 是有序的鍵值對,其中的鍵允許是任何型別。與 Set 相似,通過呼叫
Object.is()
方法來判斷重複的鍵,這意味著能將數值 5 與字串 "5" 作為兩個相對獨立的鍵。使用set()
方法能將任何型別的值關聯到某個鍵上,並且該值此後能用get()
方法提取出來。Map 也擁有一個size
屬性與一個forEach()
方法,讓專案訪問更容易。 -
Weak Map 是隻能包含物件型別的鍵的特殊 Map 。與 Weak Set 相似,鍵的物件引用是弱引用,因此當它是某個物件的僅存引用時,也不會遮蔽垃圾回收。當鍵被回收之後,所關聯的值也同時從 Weak Map 中被移除。