ES6學習筆記之Set和Map

Set

基本用法

ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。
Set 自己是一個構造函數,用來生成 Set 數據結構。數組

const setArr = new Set();

[1,2,3,4,2,2,3,4].forEach(x => setArr.add(x));

for (let i of setArr) {
  console.log(i);
}
// 1,2,3,4
複製代碼

Set 函數能夠接受一個數組(或者具備 iterable 接口的其餘數據結構)做爲參數,用來初始化。 特色:bash

  1. 以數組爲參數, 能夠去重
  2. Set 加入值的時候,不會發生類型轉換,因此4和"4"是兩個不一樣的值。
//  以數組爲參數
const set = new Set([1, 2, 3, 4, 4, '4'])
[...set]  // [1, 2, 3, 4, '4']
set.size  // 5

// 一個相似數組的帶 iterable 接口的對象
const set = new Set(document.querySelectorAll('div'))

複製代碼

Set結構轉換成數組有兩個簡單的方法數據結構

[...set]  // [1, 2, 3, 4, '4']

Array.from(set)  // [1, 2, 3, 4, '4']
複製代碼

Set 實例的屬性和方法

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

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

Set 實例的方法分爲兩大類:操做方法(用於操做數據)和遍歷方法(用於遍歷成員)。ui

四個操做方法。

  1. add(value):添加某個值,返回 Set 結構自己。
  2. delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
  3. has(value):返回一個布爾值,表示該值是否爲Set的成員。
  4. clear():清除全部成員,沒有返回值。
const s = new Set()
s.add(1).add(2).add(2)

s.size // 2

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

s.delete(2)
s.size  // 1
s.has(2) // false

複製代碼

四個遍歷方法

  1. keys():返回鍵名的遍歷器
  2. values():返回鍵值的遍歷器
  3. entries():返回鍵值對的遍歷器
  4. forEach():使用回調函數遍歷每一個成員

keys方法、values方法、entries方法返回的都是遍歷器對象。因爲 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此keys方法和values方法的行爲徹底一致。this

const set = new Set(['a', 'b', 'c']);

for (let item of set.keys()) {
  console.log(item);
}
// a
// b
// c

for (let item of set.values()) {
  console.log(item);
}
// a
// b
// c

for (let item of set.entries()) {
  console.log(item);
}
// ["a", "a"]
// ["b", "b"]
// ["c", "c"]

複製代碼

對象結構 在使用這個幾個方法的時候相似,但有必定區別spa

const obj = { 1: 'a', 2: 'b', 3: 'c' }
for (let item of Object.keys(obj)) {
  console.log(item);
}
// 1
// 2
// 3

for (let item of Object.values(obj)) {
  console.log(item);
}
// a
// b
// c

for (let item of Object.entries(obj)) {
  console.log(item);
}
// [1, "a"]
// [2, "b"]
// [3, "c"]
複製代碼

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

for (let i of set) {
  console.log(i)
}
// a
// b
// c

複製代碼

forEach(): Set 結構的實例與數組同樣,也擁有forEach方法,用於對每一個成員執行某種操做,沒有返回值。
不過keyvalue是同一個code

set.forEach((value, key) => console.log(key + ' : ' + value))
// a: a
// b: b
// c: c
複製代碼

遍歷的應用對象

  1. 去重
  2. 實現並集(Union)、交集(Intersect)和差集(Difference)
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([...union].filter(x => !intersect.has(x)));
// Set {1}
複製代碼

若是想在遍歷操做中,同步改變原來的 Set 結構,目前沒有直接的方法,但有兩種變通方法。

// 方法一
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
複製代碼

WeakSet

WeakSet 結構與 Set 相似,也是不重複的值的集合。可是,它與 Set 有兩個區別。

(1):WeakSet 能夠接受數組和相似數組的對象做爲參數。該數組的全部成員都會自動成爲WeakSet的實例對象的成員。數組成員只能是對象,不能是其餘類型的值。不然報錯。

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

const b = [1, 2, [1,2]]
new WeakSet(b)  // Uncaught TypeError: Invalid value used in weak set
複製代碼

(2):WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存,不考慮該對象還存在於 WeakSet 之中。

Map

含義和基本用法

一個常規的對象本質應該是鍵值對的合集(即Hash結構)。它的鍵應該是一個字符串。 可是有時候須要使用其餘類型好比對象來作 鍵值對的 鍵。 因而就有了Map結構

const data = {};
const element = document.getElementsByTagName('div')

data[element] = 'div';
data[element]  //  "div"
data['[object HTMLCollection]'] // "div"

// {[object HTMLCollection]: "div"}

const elementSpan = document.getElementsByTagName('span')
data[elementSpan] //  "div"

複製代碼

element 被轉化成了'[object HTMLCollection]' 只是個字符串。 並不能達到經過element 取到值的效果。可是,使用Map結構能夠。 Map結構與對象很類似也是鍵值對, 但Map 的鍵能夠不是字符串,能夠是各類類型的值(包括對象)。若是你須要「鍵值對」的數據結構,MapObject 更合適。

const dataMap = new Map()
dataMap.set(element, 'div')
dataMap.get(element)  // div

//  has,delete
dataMap.has(element)    //  true
dataMap.delete(element) //  true
dataMap.has(element)    //  false

複製代碼

以上 new Map() 實例對象爲一個Map結構,提供了setgethasdelete幾個方法。輕鬆了實現了增刪改查。 構造函數Map 能夠接受一個數組做爲參數。

const map = new Map([ ['key1', 'value1'], ['key2', 'value2'] ])

複製代碼

任何具備 Iterator 接口、且每一個成員都是一個雙元素的數組的數據結構均可以看成Map構造函數的參數。如:數組,Set結構, Map結構。

const set = new Set([['a', 1],['b', 2]]);
const m1 = new Map(set);
m1.get('a') // 1

const m2 = new Map([['c', 3]]);
const m3 = new Map(m2);
m3.get('c') // 3
複製代碼

注意:只有對同一個對象的引用,Map 結構纔將其視爲同一個鍵。

const k1 = {a: 1 }
const k2 = {a: 1 }
const map = new Map()

map.set(k1, 111)
map.get(k2) // undefined

map.set(k2, 222)
map.get(k1) //  111
map.get(k2) //  222
複製代碼

由於即使值相同,可是兩個對象內存地址是不同的。

這就解決了同名屬性碰撞(clash)的問題,咱們擴展別人的庫的時候,若是使用對象做爲鍵名,就不用擔憂本身的屬性與原做者的屬性同名。

若是 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視爲一個鍵,好比0和-0就是一個鍵。

實例的屬性和操做方法

  1. set(key, value): set方法設置鍵名key對應的鍵值爲value,而後返回整個 Map 結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵。
  2. get(key):get方法讀取key對應的鍵值,若是找不到key,返回undefined
  3. size 屬性: 返回 Map 結構的成員總數。
  4. has(key): has方法返回一個布爾值,表示某個鍵是否在當前 Map 對象之中。
  5. delete(key): delete方法刪除某個鍵,返回true。若是刪除失敗,返回false
  6. clear(): clear方法清除全部成員,沒有返回值。
const map = new Map();
//  能夠採用鏈式寫法。
map.set('a', 1).set('b', 2)

map.size          // 2

map.get('b')      // 2
map.set('b', 222)
map.get('b')      // 222
map.get('c')      // undefined

map.has('b')      // true

map.delete('b')   // true
map.has('b')      // false

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

遍歷方法

  1. keys():返回鍵名的遍歷器
  2. values():返回鍵值的遍歷器
  3. entries():返回鍵值對的遍歷器
  4. forEach():使用回調函數遍歷每一個成員

須要特別注意的是,Map 的遍歷順序就是插入順序。

const map = new Map([['a', 1], ['b',  2]]);

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

for (let value of map.values()) {
  console.log(value);
}
// 1
// 2

for (let item of map.entries()) {
  console.log(item);
}
// ["a", 1]
// ["b", 2]

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "a" 1
// "b" 2

// 等同於使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "a" 1
// "b" 2
複製代碼

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

(1)Map 與數組的互換

const arr1 = [[{'k1': 11}, 11 ],['a', 1]]
const map = new Map(arr1)

[...map]          // [[{'k1': 11}, 11 ],['a', 1]]
Array.from(map)   // [[{'k1': 11}, 11 ],['a', 1]]
複製代碼

(2) Map 與對象的互換

若是全部 Map 的鍵都是字符串,它能夠轉爲對象。

const obj1 = { a:1, b:2 }
const obj2 = {}
const map = new Map()

for(let key of Object.keys(obj1)) {
  map.set(key, obj1[key])
}

console.log(map)    // Map(2) {"a" => 1, "b" => 2}

for (let [key,value] of map) {
  obj2[key] = value
}

console.log(obj2)  // {a: 1, b: 2}
複製代碼

(3) JSON 要轉換成 Map 能夠先轉換成數組或者對象,而後再轉換。

WeakMap

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

const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"
複製代碼

WeakMap與Map的區別有兩點。

  1. WeakMap只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名。
  2. WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
const map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!

複製代碼

WeakMap 的語法:

WeakMapMap 類似但有兩個區別:

  1. 沒有遍歷操做(即沒有key()、values()和entries()方法)。
  2. 沒法清空,即不支持clear方法。 所以,WeakMap只有四個方法可用:get()、set()、has()、delete()。

WeakMap 的用途

WeakMap 應用的典型場合就是 DOM 節點做爲鍵名。 WeakMap 的另外一個用處是部署私有屬性。

const _counter = new WeakMap();
const _action = new WeakMap();

class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  dec() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}

const c = new Countdown(2, () => console.log('DONE'));

c.dec()
c.dec()

複製代碼

習題: 一· 求set 的值

let arr1 = [1, 2, 3, '3', 2]
const set = new Set(arr1)
複製代碼

二. 求遍歷的輸出

for (let [key, value] of set.entries()) {
  console.log(key === value)
}
複製代碼

三. set 轉換數成數組
四. 求 data[obj1],

const obj1 = { a: 1}
const obj2 = { b: 2}

const data = {}
data[obj1] = 11
data[obj2] = 22

// 求 data[obj1]

複製代碼

五. 1.求 map.get({a: 1}), 2.若是 map 要轉換成其餘結果,應該是對象仍是數組

const map = new Map()
map.set({a: 1}, 111)

map.get({a: 1})


複製代碼
相關文章
相關標籤/搜索