ES6入門之Set 和 Map

1. Set

1.1 基本用法

Set 相似於數組,可是成員的 值都是惟一的,沒用重複的值。Set自己是一個構造函數,用來生成Set數據結構。前端

const s = new Set();

[2,3,4,5,6,4,2,2,7].forEach(x => s.add(x))

for (let i of s) {
    console.log(i)
} // 2 3 4 5 6 7

// Set 不會添加劇復的值。
複製代碼

Set 函數能夠接受一個數組(或具備 iterable 接口的其餘數據結構)做爲參數,用來初始化,以下:算法

// 實例一
const set = new Set([1,2,3,4,5,5])
[...set] // [1,2,3,4,5]

// 實例二
const items = new Set([1,2,3,3,4])
items.size // 4

// 上面代碼還展現了數組去重的方法

[...new Set(array)]

// 或者字符串去重

[...new Set('aabbcc')].join('')
複製代碼

向 Set 加入值的時候,不會發生類型轉換, 全部**5 和 '5'**兩個不一樣的值。在Set 內部判斷兩個值是否不一樣,使用的算法叫作 'Same-value-zero equality',它和 '==='相似,區別在於 向 Set 加入值的時候認爲 NaN等於自身,而 '===' 則認爲 NaN 不等於自身segmentfault

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}
// 如上,只能向Set中添加一個NaN則說明了剛剛的證實
複製代碼

在 Set中兩個對象老是不相等的。數組

let set = new Set();
set.add({})
set.size // 1

set.add({})
set.size // 2
複製代碼

1.2 Set 實例的屬性和方法

Set 結構的實例有如下屬性數據結構

  • Set.prototype.constructor // 構造函數,默認是Set函數
  • Set.prototype.size // 返回 Set實例的成員總數。

Set 實例的方法分爲兩大類:操做方法和遍歷方法函數

Set.prototype.add(value) // 添加某個值,返回 Set 結構自己 Set.prototype.delete(value) // 刪除某個值,返回一個布爾值,表示是否刪除成功 Set.prototype.has(value) // 返回一個布爾值,表示該值是否爲Set的成員 Set.prototype.clear() // 清除全部成員,沒用返回值post

s.add(1).add(2).add(2);
// 注意2被加入了兩次

s.size // 2

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

s.delete(2);
s.has(2) // false

// Object 和 Set 如何判斷一個鍵的區別

// 對象
const p = {
    'w': 1,
    'h': 2
}
if(p[a]){
    no
}

// Set
const p = new Set()
p.add('w')
p.add('h')
if(p.has(c)){
    no
}
複製代碼

Array.from 方法能夠將 Set 結構轉爲數組,以下:this

const items = new Set([1,2,3,4,5])
const array = Array.from(items)

// 數組去重的另一種方法

function d(a){
    return Array.from(new Set(a))
}
d([1,2,3,3,5])
複製代碼

1.3 遍歷操做

Set 結構的實例有四個遍歷方法,用於遍歷成員spa

  • Set.prototype.keys() // 返回鍵名的遍歷器
  • Set.prototype.values() // 返回鍵值的遍歷器
  • Set.prototype.entries() // 返回鍵值對的遍歷器
  • Set.prototype.forEach() // 使用回調函數遍歷每一個成員

注意:Set遍歷順序就是插入順序,這個特性在特定狀況很是有用,好比使用Set保存一個回調函數列表,調用時就能保證按照添加順序調用。prototype

1.3.1 keys()、values、entries()

上面三個都是返回遍歷器對象,因爲Set結構沒用鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此以上方法的行爲徹底一致

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"]
複製代碼

注意:Set結構的實例默承認遍歷,它的默認遍歷器生成函數就是它的 values方法,這樣咱們就能夠省略 values,直接用 for...of循環遍歷Set

1.3.2 forEach()

Set結構的實例和數組同樣,也有 forEach方法,用於對每一個成員執行某種操做,沒用返回值。forEach 能夠用第二個參數表示綁定處理函數內部的 this 對象。

1.3.3 遍歷的應用

擴展運算符(...)內部使用 for...of循環樣能用於 Set 結構

let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]用來去重操做

// 實現 並集、交集、差集
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}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
複製代碼

若是想在遍歷操做中同步改變原理的Set結構,只能利用原有的Set結構映射一個新的結構,而後賦值給原來的Set,另外一個就是經過 Array.from方法。

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
複製代碼

2. WeakSet

WeakSet 結構與Set相似,也不是重複的值的集合,可是和Set有兩個區別,第一個它的成員只能爲對象,另外一個它的對象都是弱印象,即垃圾回收機制不考慮 WeakSet對該對象的引用,通俗的講就是,若是該對象沒用在其餘對象中被引用,那麼該對象就會被回收,不會考慮這個對象是否在 WeakSet中。

依賴於垃圾回收進制依賴引用計數,若是一個值的引用次數不爲0,那麼就不會被回收,可是有的時候,結束使用該值後,會忘記取消引用,就會致使內存沒法釋放從而致使內存泄漏。可是 WeakSet裏面的而引用不會計入垃圾回收機制,因此適合存放臨時的對象,一旦外部消失,那麼WeakSet裏面的引用就會自動消失。

基於以上的特色,WeakSet 成員不適合被引用,因此 WeakSet沒法被遍歷。

2.1 語法

它也是一個構造函數,能夠經過 new 來建立

const ws = new WeakSet()

// 作爲構造函數,WeakSet

能夠接受一個數組或相似數組的對象做爲參數,該數組的全部成員,
都會自動成爲 WeakSet實例對象的成員。
複製代碼

注意:只能是數組的成員成爲WeakSet的成員,而不是 a 數組自己,這就意味着,數組的成員只能是對象。

2.2 WeakSet的方法

  1. WeakSet.prototype.add(value): 向 WeakSet實例添加一個新成員。
  2. WeakSet.prototype.delete(value): 清除 WeakSet實例的指定成員。
  3. WeakSet.prototype.has(value): 返回一個布爾值,表示某個值是否存在 WeakSet實例中。

注意: WeakSet 一樣沒有size 屬性,不能遍歷其成員。

3. Map

JavaScript的對象,本質上是鍵值對的集合,可是傳統上只能字符串當作鍵,這給他帶來了很大的限制。Map的出現,就是讓各類類型的值均可以看成鍵。Map提供的是 「值-值」的對應。

const map = new Map([
  ['name', '張三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"
複製代碼

注意:Set 和 Map 均可以用來生成新的Map,若是對同一個鍵屢次賦值,那麼前面的將被後面的值覆蓋。另外只有對同一個對象的引用,Map結構纔將其視爲同一個鍵。另外一樣的兩個實例,在Map中將被視爲兩個鍵。

**總結:**綜上所述,Map的鍵實際上跟內存地址綁定的,只要內存地址不同,就視爲兩個鍵。這樣就能夠解決同名屬性碰撞的問題。若是咱們擴展別人庫的時候,若是使用對象最爲鍵名,就不用擔憂本身的屬性與原做者屬性衝突。

若是Map的鍵是一個簡單類型的數值,則只要兩個值嚴格相等,Map將其視爲一個鍵,0 和 -0 是一個鍵,true 和 'true'則是兩個不一樣的鍵, undefined 和 null 也是兩個不一樣的鍵, 另外 NaN 在Map 中視爲同一個鍵

3.1 Map的屬性和操做方法

1. size 屬性

size 屬性返回Map結構的成員總數

const map = new Map()
map.set('foo', ture)
map.set('bar', false)

map.size // 2
複製代碼
2. Map.prototype.set(key, value)

set 方法設置鍵名 key 對應的鍵值爲 value,而後返回整個 Map 結構。若是 key 已經有值,則鍵值會被更新,不然就新生成該鍵。

const m = new Map()
m.set('e', 6)   // 鍵值是字符串
m.set(2, 's')   // 鍵是數值
m.set(undefined, 'n')   // 鍵是 undefined

// set方法返回的是當前的Map對象,所以能夠採用鏈式寫法

let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
複製代碼
3. Map.prototype.get(key)

get 方法讀取 key 對應的鍵值,若是找不到key就返回 undefined

const m = new Map();

m.set('c',  124)
m.get('c')  // 124
複製代碼
4. Map.prototype.has(key)

返回一個布爾值,用來表示某個鍵是否在當前 Map 對象中

let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')

map.has(1)  // true
map.has(4)  // false
複製代碼
5. Map.prototype.delete(key)

delete方法刪除某個鍵,返回 true,若是刪除失敗,則返回 false

let map = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c')

map.delete(1)   // true
map.delete(4)   // false
複製代碼
6. Map.prototype.clear()

clear 方法清除全部成員,沒有返回值

let map = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c')

map.size    // 3
map.clear() 
map.size    // 0
複製代碼

3.2 遍歷方法

  1. Map.prototype.keys(): 返回鍵名的遍歷器
  2. Map.prototype.values(): 返回鍵值的遍歷器
  3. Map.prototype.entries(): 返回全部成員的遍歷器
  4. Map.prototype.forEach(): 遍歷Map的全部成員

注意:Map的遍歷順序就是插入順序。

Map 結構轉爲數組結構,比較快速的方法是使用擴展運算符(...),另外Map能夠經過 forEach 能夠實現遍歷。

const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

[...map.keys()]
// [1, 2, 3]

[...map.values()]
// ['one', 'two', 'three']

[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
複製代碼

3.3 與其餘數據結構的互相轉換

1. Map轉爲數組

經過擴展運算符(...)

2. 數組轉爲Map

將數組 傳入 Map構造函數,就能夠轉爲Map

new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])
複製代碼
3. Map 轉爲對象

若是Map的鍵都是字符串,它能夠無損地轉爲對象,若是有非字符串的鍵名,那麼這個鍵名會被轉成字符串,再做爲對象的鍵名。

4. 對象轉爲 Map
5. Map 轉爲 JSON

Map轉爲JSON要區分兩種狀況。一種狀況是,Map 的鍵名都是字符串,這時能夠選擇轉爲對象JSON。另一種狀況是,Map 的鍵名有非字符串,這時能夠選擇轉爲數組JSON

6. JSON 轉爲 Map

JSON轉爲Map,正常狀況下,全部鍵名都是字符串。可是,有一種特殊狀況,整個JSON就是一個數組,且每一個數組成員自己,又是一個有兩個成員的數組。這時,它能夠一一對應地轉爲Map.

4. WeakMap

WeakMap 結構與 Map 結構相似,也是用於生成鍵值對的集合

WeakMap 和 Map 的區別

1. WeakMap只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名。
2. WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
複製代碼

若是 咱們想在某個對象上面存放以未數據,可是會造成對於這個對象的引用,若是咱們不須要這兩個對象,就必須手動刪除,不然垃圾回收機制就不會釋放佔用的內存。

WeakMap 就是爲了解決這個問題而誕生,它的鍵名所引用的對象都是弱引用,即垃圾回收機制不將該引用考慮在內。所以,只要所引用的對象的其餘引用被清除,垃圾回收機制就會釋放該對象所佔用的內存,也就是說一旦再也不須要,WeakMap裏面的鍵名對象和所對應的鍵值對會自動消失,不用手動刪除。

注意: WeakMap弱引用的只是鍵名,而不是鍵值。鍵值依然是正常引用。

4.1 WeakMap 的語法

WeakMap 與 Map 在 API的區別主要有兩個,一是沒有遍歷操做(沒有keys,values,entries),也沒有size屬性。由於沒有辦法列出全部鍵名,某個鍵名是否存在徹底不可預測,跟垃圾回收機制是否運行相關。這一刻能夠取到鍵名,下一個垃圾回收機制忽然運行了,這個鍵名就沒有了。爲了防止不肯定性,就統一規定不能取到鍵名。二是沒法狀況,即不支持 clear 方法。所以 WeakMap 有四個方法: get()、set()、has()、delete()

const wm = new WeakMap();

// size、forEach、clear 方法都不存在
wm.size // undefined
wm.forEach // undefined
wm.clear // undefined
複製代碼

4.2 WeakMap的用途

  1. 應用於 DOM 節點做爲鍵名
  2. WeakMap 的另外一個用處是部署私有屬性
  3. 就是防止內存泄漏的風險

歡迎關注 公衆號【小夭同窗】

重學js系列

重學js之JavaScript簡介

重學 JS 之在 HTML 中使用 JavaScript

重學js之JavaScript基本概念(上)=> 數據類型

重學js之JavaScript基本概念(中)=> 操做符

重學js之JavaScript基本概念(下)=> 運算符

重學js之JavaScript變量、做用域和內存問題

ES6入門系列

ES6入門之let、cont

ES6入門之變量的解構賦值

ES6入門之字符串的擴展

ES6入門之正則的擴展

ES6入門之數值的擴展

ES6入門之函數的擴展

ES6入門之數組的擴展

ES6入門之對象的擴展

ES6入門之對象的新增方法

ES6入門之Symbol

Git教程

前端Git基礎教程

相關文章
相關標籤/搜索