ES6新的數據結構Set和Map總結

Set、Map、WeakSet 和 WeakMap

set(集合)和map(字典)這兩種數據結構的應用場景在於數據重組數據存儲javascript

一、Set(惟1、無序、沒有重複)

ES6 新增的一種新的數據結構,相似於數組,但成員是惟一且無序的,沒有重複的值(Set 對象容許你儲存任何類型的惟一值,不管是原始值或者是對象引用java

  • 基本使用
const s = new Set()
[1, 2, 3, 4, 3, 2, 1].forEach(x => s.add(x))

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

// 去重數組的重複對象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)]	// [1, 2, 3]
複製代碼
  • 向 Set 加入值的時候,不會發生類型轉換,因此5"5"是兩個不一樣的值
  • Set 內部判斷兩個值是否不一樣,使用的算法叫作「Same-value-zero equality」,它相似於精確相等運算符(===),主要的區別是NaN等於自身,而精確相等運算符認爲NaN不等於自身
let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

let set1 = new Set()
set1.add(5)
set1.add('5')
console.log([...set1])	// [5, "5"]
複製代碼
  • 屬性和方法算法

    • size:元素數量
    • add(value):新增,至關於 array裏的push
    • delete(value):存在即刪除集合中value
    • has(value):判斷集合中是否存在 value
    • clear():清空集合
    let set = new Set()
    set.add(1).add(2).add(1)
    set.size // 2
    set.has(1)	// true
    set.has(3)	// false
    set.delete(1)	
    set.has(1)	// false
    複製代碼
    • Array.from 方法能夠將 Set 結構轉爲數組json

      const items = new Set([1, 2, 3, 2])
      const array = Array.from(items)
      console.log(array)	// [1, 2, 3]
      // 或
      const arr = [...items]
      console.log(arr)	// [1, 2, 3]
      複製代碼
  • 遍歷方法(遍歷順序爲插入順序)數組

    • keys():返回一個包含集合中全部鍵的迭代器(由於set加入的值沒有key,因此返回的是value值)數據結構

    • values():返回一個包含集合中全部值得迭代器函數

    • entries():返回一個包含Set對象中全部元素得鍵值對迭代器ui

    • forEach(callbackFn, thisArg):用於對集合成員執行callbackFn操做,若是提供了 thisArg 參數,回調中的this會是這個參數,沒有返回值this

      let set = new Set([1, 2, 3])
      console.log(set.keys())	// SetIterator {1, 2, 3}
      console.log(set.values())	// SetIterator {1, 2, 3}
      console.log(set.entries())	// SetIterator {1, 2, 3}
      
      for (let item of set.keys()) {
        console.log(item);
      }	// 1 2 3
      for (let item of set.entries()) {
        console.log(item);
      }	// [1, 1] [2, 2] [3, 3]
      
      set.forEach((value, key) => {
          console.log(key + ' : ' + value)
      })	// 1 : 1 2 : 2 3 : 3
      console.log([...set])	// [1, 2, 3]
      複製代碼
    • Set 可默認遍歷,默認迭代器生成函數是 values() 方法,可使用map、filter等方法spa

      Set.prototype[Symbol.iterator] === Set.prototype.values	// true
      
      let set = new Set([1, 2, 3])
      set = new Set([...set].map(item => item * 2))
      console.log([...set])	// [2, 4, 6]
      
      set = new Set([...set].filter(item => (item >= 4)))
      console.log([...set])	//[4, 6]
      複製代碼
    • Set 很容易實現交集(Intersect)、並集(Union)、差集(Difference)

      let set1 = new Set([1, 2, 3])
      let set2 = new Set([4, 3, 2])
      
      let intersect = new Set([...set1].filter(value => set2.has(value)))
      let union = new Set([...set1, ...set2])
      let difference = new Set([...set1].filter(value => !set2.has(value)))
      
      console.log(intersect)	// Set {2, 3}
      console.log(union)		// Set {1, 2, 3, 4}
      console.log(difference)	// Set {1}
      複製代碼

2. WeakSet

WeakSet 對象容許你將弱引用對象儲存在一個集合中

WeakSet 與 Set 的區別:

  • WeakSet 只能儲存對象引用不能存放值,而 Set 對象均可以

  • WeakSet 對象中儲存的對象值都是被弱引用的,即垃圾回收機制不考慮 WeakSet 對該對象的應用,若是沒有其餘的變量或屬性引用這個對象值,則這個對象將會被垃圾回收掉(不考慮該對象還存在於 WeakSet 中),因此,WeakSet 對象裏有多少個成員元素,取決於垃圾回收機制有沒有運行,運行先後成員個數可能不一致,遍歷結束以後,有的成員可能取不到了(被垃圾回收了),WeakSet 對象是沒法被遍歷的(ES6 規定 WeakSet 不可遍歷),也沒有辦法拿到它包含的全部元素

  • 屬性和方法的區別:沒有size屬性和clear方法

    var ws = new WeakSet()
    var obj = {}
    var foo = {}
    
    ws.add(window)
    ws.add(obj)
    
    ws.has(window)	// true
    ws.has(foo)	// false
    
    ws.delete(window)	// true
    ws.has(window)	// false
    複製代碼

三、Map

集合 與 字典 的區別:

  • 共同點:集合、字典 能夠儲存不重複的值
  • 不一樣點:集合 是以 [value, value]的形式儲存元素,字典 是以 [key, value] 的形式儲存
const m = new Map()
const o = {p: 'haha'}
m.set(o, 'content')
m.get(o)	// content

m.has(o)	// true
m.delete(o)	// true
m.has(o)	// false
複製代碼

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

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

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

只有對同一個對象的引用,Map 結構纔將其視爲同一個鍵。這一點要很是當心

const map = new Map();

map.set(['a'], 555);
map.get(['a']) // undefined
複製代碼

上面代碼的setget方法,表面是針對同一個鍵,但實際上這是兩個值,內存地址是不同的,所以get方法沒法讀取該鍵,返回undefined

由上可知,Map 的鍵其實是跟內存地址綁定的,只要內存地址不同,就視爲兩個鍵。這就解決了同名屬性碰撞(clash)的問題,咱們擴展別人的庫的時候,若是使用對象做爲鍵名,就不用擔憂本身的屬性與原做者的屬性同名。

若是 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視爲一個鍵,好比0-0就是一個鍵,布爾值true和字符串true則是兩個不一樣的鍵。另外,undefinednull也是兩個不一樣的鍵。雖然NaN不嚴格相等於自身,但 Map 將其視爲同一個鍵

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123
複製代碼

屬性和方法

  • size:返回字典中所包含的元素個數

    const map = new Map([
      ['name', 'An'],
      ['des', 'JS']
    ]);
    
    map.size // 2
    複製代碼
  • set(key, value):向字典中添加新元素

  • get(key):經過鍵查找特定的數值並返回

  • has(key):判斷字典中是否存在鍵key

  • delete(key):經過鍵 key 從字典中移除對應的數據

  • clear():將這個字典中的全部元素刪除

遍歷方法

  • Keys():將字典中包含的全部鍵名以迭代器形式返回
  • values():將字典中包含的全部數值以迭代器形式返回
  • entries():返回全部成員的迭代器
  • forEach():遍歷字典的全部成員

Map 結構的默認遍歷器接口(Symbol.iterator屬性),就是entries方法。

map[Symbol.iterator] === map.entries
// true
複製代碼
const reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value);
  }
};

let map = new Map([
    ['name', 'An'],
    ['des', 'JS']
])
map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);
// Key: name, Value: An
// Key: des, Value: JS
複製代碼

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

  • Map 轉 Array

    const map = new Map([[1, 1], [2, 2], [3, 3]])
    console.log([...map])	// [[1, 1], [2, 2], [3, 3]]
    複製代碼
  • Array 轉 Map

    const map = new Map([[1, 1], [2, 2], [3, 3]])
    console.log(map)	// Map {1 => 1, 2 => 2, 3 => 3}
    複製代碼
  • Map 轉 Object

    function mapToObj(map) {
        let obj = Object.create(null)
        for (let [key, value] of map) {
            obj[key] = value
        }
        return obj
    }
    const map = new Map().set('name', 'An').set('des', 'JS')
    mapToObj(map)  // {name: "An", des: "JS"}
    複製代碼
  • Object 轉 Map

    function objToMap(obj) {
        let map = new Map()
        for (let key of Object.keys(obj)) {
            map.set(key, obj[key])
        }
        return map
    }
    
    objToMap({'name': 'An', 'des': 'JS'}) // Map {"name" => "An", "des" => "JS"}
    複製代碼
  • Map 轉 JSON

    function mapToJson(map) {
        return JSON.stringify([...map])
    }
    
    let map = new Map().set('name', 'An').set('des', 'JS')
    mapToJson(map)	// [["name","An"],["des","JS"]]
    複製代碼
  • JSON 轉 Map

    function jsonToStrMap(jsonStr) {
      return objToMap(JSON.parse(jsonStr));
    }
    
    jsonToStrMap('{"name": "An", "des": "JS"}') // Map {"name" => "An", "des" => "JS"}
    複製代碼

四、WeakMap

WeakMap 對象是一組鍵值對的集合,其中的鍵是弱引用對象,而值能夠是任意

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

WeakMap 中,每一個鍵對本身所引用對象的引用都是弱引用,在沒有其餘引用和該鍵引用同一對象,這個對象將會被垃圾回收(相應的key則變成無效的),因此,WeakMap 的 key 是不可枚舉的。

屬性和方法的區別是:沒有size屬性和clear方法

5. 總結

  • Set
    • 成員惟1、無序且不重複
    • [value, value],鍵值與鍵名是一致的(或者說只有鍵值,沒有鍵名)
    • 能夠遍歷,方法有:add、delete、has
  • WeakSet
    • 成員都是對象
    • 成員都是弱引用,能夠被垃圾回收機制回收,能夠用來保存DOM節點,不容易形成內存泄漏
    • 不能遍歷,方法有add、delete、has
  • Map
    • 本質上是鍵值對的集合,相似集合
    • 能夠遍歷,方法不少能夠跟各類數據格式轉換
  • WeakMap
    • 只接受對象做爲鍵名(null除外),不接受其餘類型的值做爲鍵名
    • 鍵名是弱引用,鍵值能夠是任意的,鍵名所指向的對象能夠被垃圾回收,此時鍵名是無效的
    • 不能遍歷,方法有get、set、has、delete
相關文章
相關標籤/搜索