ES6. Set、WeakSet、Map、WeakMap

151 阅读5分钟

1. Set

  • Set中存放的元素是不会重复,创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式
  • Set中存放一个对象let a={name:'seikonn'};set.add(a)时,实际存放的是该对象的引用,及存放了a的地址,在堆内存中的{name:'seikonn'}被变量a和set同时指向,变量a发生变化a={},并不会影响set中的元素。
    const set = new Set(1, 2, 3) //x
    const set = new Set([1, 2, 3]) //通过数组创建,或者使用add方法set.add(item)
    
    let obj1 = { name: "seikonn" }
    const set = new Set()
    set.add(obj1)
    console.log(set)
    
    obj1 = {name: '1'}
    console.log(set) //两次打印相同
    

1.1 属性

size:返回Set中元素的个数;

1.2 方法

add(value):添加某个元素,返回Set对象本身;
delete(value):从set中删除和这个值相等的元素,返回boolean类型;
has(value):判断set中是否存在某个元素,返回boolean类型;
clear():清空set中所有的元素,没有返回值;
forEach(callback, [, thisArg]):通过forEach遍历set;for of 也可遍历

1.3 数组去重

const arr = [10, 20, 10, 44, 78, 44]
const set = new Set(arr)
const newArray1 = [...set]
const newArray2 = Array.form(set)

2. WeakSet

  • 和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构。

2.1 weakSet和set的区别

  1. WeakSet中只能存放对象类型,不能存放基本数据类型;
  2. WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收;例:set中存放一个对象let a={name:'seikonn'};set.add(a)时,实际存放的是该对象的引用,及存放了a的地址,在堆内存中的{name:'seikonn'}被变量a和set同时指向,变量a发生变化a={},并不会影响set中的元素。如果使用weakSet,当a={}时,堆内存中的{name:'seikonn'}不被a指向了且weakSet中的元素对{name:'seikonn'}为弱引用,此时堆内存中的{name:'seikonn'}将被GC回收,weakSet中将也不会存放该对象;
  3. 没有size属性,clear()方法,forEach()方法,不能遍历,因为WeakSet只是对对象的弱引用,如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁;
  4. 存储到WeakSet中的对象是没办法获取的;

2.2 方法

add(value):添加某个元素,返回WeakSet对象本身;
delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
has(value):判断WeakSet中是否存在某个元素,返回boolean类型;

2.3 应用场景

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对象清空,weakset里面也会自动移除(弱引用),被GC销毁

p.running.call({name: "why"}) // Error("不能通过非构造方法创建出来的对象调用running方法")

3. Map

  • 用于存储映射关系

  • map.set(key, value)创建,或 new Map()传入一个ES8的Entries( [ [key,value], [key,value] ] )

  • map和set一样,为强引用,即key和value为对象时,并不会因为源对象发生改变而改变map和set里的元素

    let obj1 = { name: "why" }
    let obj2 = { name: "seikonn" }
    const map = new Map()
    map.set(obj1, obj2)
    console.log(map)
    
    obj1 = {name: '1'}
    obj2 = {name: '2'}
    console.log(map) // 两次打印结果相同
    

3.1 与对象存储映射关系区别

  1. 事实上对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);
  2. 某些情况下我们可能希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key;
    const obj1 = { name: "why" }
    const obj2 = { name: "kobe" }
    
    const info = {
      [obj1]: "aaa", //变量obj1作为key
      [obj2]: "bbb"  //变量obj2作为key
    }
    console.log(info) // {[object Object]: "bbb"},只存在一个
    
    const map1 = new Map()
    map1.set(obj1, "aaa")
    map1.set(obj2, "bbb")
    map1.set(1, "ccc")
    const map2 = new Map([
        [obj1, 'aaa'],
        [obj2, 'bbb'],
        [1, 'ccc']
    ])
    console.log(map1) //  {{name: 'why'} => 'aaa', {name: 'kobe'} => 'bbb', 1 => 'ccc'}
    console.log(map2) //  {{name: 'why'} => 'aaa', {name: 'kobe'} => 'bbb', 1 => 'ccc'}
    
    1.png

3.2 属性

size:返回Map中元素的个数;

3.3 方法

set(key, value):在Map中添加key、value,并且返回整个Map对象;
get(key):根据key获取Map中的value;
has(key):判断是否包括某一个key,返回Boolean类型;
delete(key):根据key删除一个键值对,返回Boolean类型;
clear():清空所有的元素;
forEach(callback, [, thisArg]):通过forEach遍历Map;for of 也可遍历

3.4 map遍历

map2.forEach((value, key) => {
  console.log(value, key)
})

for (const item of map2) { // item为数组,第一项为key,第二项为value
  console.log(item[0], item[1])
}

for (const [key, value] of map2) {
  console.log(key, value)
}

4. WeakMap

和Map类型的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的

4.1 WeakMap和Map的区别

  1. WeakMap的key只能存放对象类型,不能存放基本数据类型;
  2. WeakMap的key对对象的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;
  3. 没有size属性,clear()方法,forEach()方法,不能遍历

4.2 方法

set(key, value):在Map中添加key、value,并且返回整个Map对象;
get(key):根据key获取Map中的value;
has(key):判断是否包括某一个key,返回Boolean类型;
delete(key):根据key删除一个键值对,返回Boolean类型;

4.3 应用场景

  • 响应式原理
    const obj1 = {
      name: "why",
      age: 18
    }
    function obj1NameFn1() {
      console.log("obj1NameFn1被执行")
    }
    function obj1NameFn2() {
      console.log("obj1NameFn2被执行")
    }
    function obj1AgeFn1() {
      console.log("obj1AgeFn1")
    }
    function obj1AgeFn2() {
      console.log("obj1AgeFn2")
    }
    
    const obj2 = {
      name: "kobe",
      height: 1.88,
      address: "广州市"
    }
    function obj2NameFn1() {
      console.log("obj1NameFn1被执行")
    }
    function obj2NameFn2() {
      console.log("obj1NameFn2被执行")
    }
    
    /*
      当数据发生改变是指向指定函数
    */
    // 1.创建WeakMap
    const weakMap = new WeakMap()
    
    // 2.收集依赖结构
    // 2.1.对obj1收集的数据结构
    const obj1Map = new Map()
    obj1Map.set("name", [obj1NameFn1, obj1NameFn2])
    obj1Map.set("age", [obj1AgeFn1, obj1AgeFn2])
    weakMap.set(obj1, obj1Map)
    
    // 2.2.对obj2收集的数据结构
    const obj2Map = new Map()
    obj2Map.set("name", [obj2NameFn1, obj2NameFn2])
    weakMap.set(obj2, obj2Map)
    
    // 3.如果obj1.name发生了改变,用Proxy/Object.defineProperty监听
    obj1.name = "james"
    // 执行一下
    const targetMap = weakMap.get(obj1)
    const fns = targetMap.get("name")
    fns.forEach(item => item())