0%

每天一點點,學習ES6(六)數據結構 Set Map

前言

昨天剛研究完另一種數據結構Symbol
趁記憶猶新的時候,把 SetMap一起解決!!

yes

在ES6之前,儲存數據的結構主要有兩種:

  • Array
  • Object

在ES6中新增了另外兩種數據結構: SetMap
以及它们的另外形式WeakSetWeakMap

Set

Setes6新增的數據結構,類似於樹組,
但是裡面的值都是唯一的,沒有重複的值,我們一般稱為集合

Set本身是一个構造函數,用來生成 Set 數據結構

1
const s = new Set();

- 構造函數的三大特點

構造函數的函數名的第一個字母通常大寫。
函數體內使用this關鍵字,代表所要生成的對象實例。
生成對象的時候,必須使用new命令來調用構造函數。

Set 的常見方法

Set常見的屬性:

  • size:返回Set中元素的個數;
    Set常用的方法:

  • add(value):添加某個元素,返回Set對象本身;

  • delete(value):從set中刪除和這個值相等的元素,返回Boolean類型;

  • has(value):判斷set中是否存在某個元素,返回Boolean類型;

  • clear():清空set中所有的元素,沒有返回值;

  • forEach(callback, [ thisArg]):通過forEach遍歷set;

size

1
s.size // 0   <-----返回裡面的length

add(value)

添加某個值,返回 Set 結構本身

當添加已經存在的元素,set不會進行處理添加

1
2
s.add(1).add(2).add(2); // 2只被加了一次
//Set(2) {1, 2}

delete(value)

删除某個值,返回一個布爾值(True or False),表示删除是否成功

1
s.delete(1) //false

has(value)

返回一个(True or False),判斷該值是否為Set的元素

1
s.has(2) //false

clear()

清除Set內所有元素,沒有返回值

1
s.clear()

遍歷

Set實現遍歷的方法有下:

  • **keys()**:返回key值的遍歷器
  • **values()**:返回value值的遍歷器
  • **entries()**:返回entries的遍歷器
  • **forEach()**:使用回调函數遍歷每個元素

Set的遍歷順序就是插入順序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

forEach()用於對每個成員執行某種操作,沒有返回值,鍵值、鍵名都相等,
同樣的forEach方法有第二個參數,用於綁定處理函數的this

1
2
3
4
5
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9

擴展運算符Set 結構相結合實現數組或字符串去重

1
2
3
4
5
6
7
// Array
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)]; // [3, 5, 2]

// String
let str = "352255";
let unique = [...new Set(str)].join(""); // "352"

實現並集交集、和差集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

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

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

// (a 相對於 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

Map

Map類型是key值對的有序列表,而keyvalue都可以是任意類型

Map本身是一個構造函數,用來生成 Map 數據結構

1
const m = new Map()

Map的常見方法

Map常見的屬性:

  • size:返回Map中元素的個數;
    Map常用的方法:

  • set(key, value):在Map中添加keyvalue,並且返回整個Map對象

  • get(key):根據key獲取Map中的value;

  • delete(value):從Map中刪除和這個值相等的元素,返回Boolean類型;

  • has(value):判斷Map中是否存在某個元素,返回Boolean類型;

  • clear():清空Map中所有的元素,沒有返回值;

  • forEach(callback, [ thisArg]):通過forEach遍歷Map;

Size

size屬性返回Map 結構的元素總數。

1
2
3
4
5
const map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2

set()

設置key對應的鍵值為value,然後返回整個Map 結構

如果key已經有值,則value值會被更新,否則就新生成該value

同時返回的是當前Map對象,可採用鍊式寫法

1
2
3
4
5
6
const m = new Map();

m.set('edition', 6) // key是字符串
m.set(262, 'standard') // key是數值
m.set(undefined, 'nah') // key是 undefined
m.set(1, 'a').set(2, 'b').set(3, 'c') // 鏈式寫法

get()

get方法讀取key對應的value值,如果找不到key,返回undefined

1
2
3
4
5
6
const m = new Map();

const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // key是函数Function

m.get(hello) // Hello ES6!

has()

has方法返回一個Boolean,表示某個key是否在當前Map 對象之中

1
2
3
4
5
6
7
8
9
10
const m = new Map();

m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');

m.has('edition') // true
m.has('years') // false
m.has(262) // true
m.has(undefined) // true

delete()

delete方法刪除某個key,返回true。如果刪除失敗,返回false

1
2
3
4
5
6
const m = new Map();
m.set(undefined,'nah');
m.has(undefined) //true

m.delete(undefined)
m.has(undefined) //false

clear()

clear方法清除所有元素,沒有返回值

1
2
3
4
5
6
7
let map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2
map.clear()
map.size // 0

遍歷

Map結構原生提供三個遍歷器生成函數一個遍歷方法

  • **keys()**:返回key的遍歷器
  • **values()**:返回value的遍歷器
  • **entries()**:返回所有元素的遍歷器
  • **forEach()**:遍歷Map 的所有元素
    遍歷順序就是插入順序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);

for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"

map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});

// Key: F, Value: no
// Key: T, Value: yes

複製合併

clone

1
2
3
4
5
6
7
8
var original = new Map([
[1, 'one']
]);

var clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison

合併

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);

var second = new Map([
[1, 'uno'],
[2, 'dos']
]);

// Merge two maps. The last repeated key wins.
// Spread operator essentially converts a Map to an Array
var merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

Map也可以跟 Array 合併:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);

var second = new Map([
[1, 'uno'],
[2, 'dos']
]);

// Merge maps with an array. The last repeated key wins.
var merged = new Map([...first, ...second, [1, 'eins']]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

WeakSetWeakMap

WeakSet

Set類似的另外一個數據結構稱之為WeakSet
也是內部元素不能重複的數據結構

Set有何差異

  • WeakSet中只能存放引用類型,不能是其他類型的值;

    • 引用類型統稱為object 類型,細分的話有:Object 類型、Array 類型、Date 類型、RegExp 類型、Function 類型
  • WeakSet對元素的引用是弱引用,如果沒有其他引用對某個對象進行引用,那麼垃圾回收機制可以對該對象進行回收;

舉個例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

let ws=new WeakSet();

// 元素不是引用類型
let weakSet=new WeakSet([2,3]);
console.log(weakSet)
// 報錯!!!
// Uncaught TypeError: Invalid value used in weak set
// at WeakSet.add (<anonymous>)
// at new WeakSet (<anonymous>)
// at <anonymous>:4:13


// 元素為引用類型
let obj1={name:1}
let obj2={name:1}
let ws=new WeakSet([obj1,obj2]);
console.log(ws) //WeakSet {{…}, {…}}


//--------------------------------------------
let a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}

a = "";

console.log(ws) //WeakSet{}


// weakSet a為null ,weakSet就會被垃圾回收機制一起清空 ---->所謂的弱型別

//--------------------------------------------
let a = [[1, 2], [3, 4]];
const ws = new Set(a);

console.log(ws) // Set(2) {Array(2), Array(2)}

a="";

console.log(ws) // Set(2) {Array(2), Array(2)}

// Set a 為null ,不會被清空 ----> 所謂的強型別

來舉一個實際 Stack Overflow 的案例

不能通過非構造方法創建出來的對象調用構造函數的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const personSet = new WeakSet()
class Person {
constructor() {
personSet.add(this)
}

running() {
if (!personSet.has(this)) {
throw new Error("不能通过非构造方法创建出来的对象调用running方法")
}
console.log("running~", this)
}
}

let p = new Person()
p.running()
p = null

p.running.call({name: "why"})

WeakMap

Map類型的另外一個數據結構稱之為WeakMap
也是以key值對的形式存在的。

在API中WeakMap與Map有兩個區別

  • 沒有遍歷操作的API
  • 沒有clear清空方法
1
2
3
4
5
6
7
8
9
10
11
12
13
// WeakMap 可以使用 set 方法添加元素
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2

// WeakMap 也可以接受一个Array,
// 作为構造函數的参数
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"

WeakMap只接受Obj作為key(null除外),不接受其他類型的值作為key

1
2
3
4
5
6
7
const map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key

WeakMapkey所指向的Obj,一旦不再需要,裡面的key和所對應的value會自動消失,不用手動刪除

舉個例子: 在網頁的DOM 元素上添加數據,就可以使用WeakMap結構,當該DOM 元素被清除,其所對應的WeakMap`記錄就會自動被移除

1
2
3
4
5
6
const wm = new WeakMap();

const element = document.getElementById('example');

wm.set(element, 'some information');
wm.get(element) // "some information"

注意:WeakMap弱引用的只是key,而不是valuekey依然是正常引用

下面代碼中,key值obj會在WeakMap產生新的引用,當你修改obj不會影響到內部value

1
2
3
4
5
6
7
8
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};

wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}

結論

新增了兩個set&map數據結構,找資料發現還有他們對應的弱型別-.-+
搞得一個頭兩個大!!!

找了很多範例,終於是有一點點點點的明白

希望大家也清楚了^_______________^

清楚


References

你的支持是我活力的來源 :)