跟着coderwhy学习JavaScript高级(十六)

285 阅读5分钟

ES6

Set

  • 在ES6之前,我们存储数据的结构主要有两种:数组、对象。

    • 在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。
  • Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组区别元素不能重复

    • 创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式):
  • Set常见的属性和方法

    • size 返回Set中元素的个数。[属性]
    • add(value) 添加某个Set元素,并返回对象本身。
    • delete(key):根据key删除一个键值对,返回Boolean类型。
    • has(value) 判断某个元素是否存在,返回boolean类型。
    • clear() 清空所有元素,没有返回值。
    • forEach() 遍历
  • 使用场景

    • 数组去重

WeakSet

  • 和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构。
  • 那么和Set有什么区别呢?
    • 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型;
    • 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收;
    • 无法遍历
  • 常见方法
    • add(value):添加某个元素,返回WeakSet对象本身;
    • delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
    • has(value):判断WeakSet中是否存在某个元素,返回boolean类型;
  • 使用场景(了解即可)
      // Stack Overflow
      const pwset = new WeakSet();
    
      class Person {
        // new的时候会执行constructor里面的代码
        constructor() {
          pwset.add(this)
        }
    
        running() {
          if (!pwset.has(this)) {
            throw new Error('不能通过其他对象来调用running方法')
          }
          console.log("running", this)
        }
      }
    
      var p = new Person();
      console.log(p.running())
      // 会报错
      p.running.call({ })
      
      // 这里为什么使用WeakSet呢? 
      // 因为WeakSet是弱引用 我们把p = null 后 就会被GC回收
      // 如果使用Set 就会是强引用 ,我们把p = null 后 还要 set.delete(p) 才会被GC回收
    
    

强引用和弱引用的区别

  • 弱引用 弱引用与强引用相对, 一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。因此使用弱引用可以防止内存泄漏。在JavaScript中弱引用的WeakMapWeakSet

image.png

  • 强引用 JavaScript中最常见的就是声明一个变量并且将一个引用类型数据(对象)赋值给这个变量,这种情况就是强引用。只要该对象还被引用,垃圾回收机制GC就不会回收该对象或者属性。对于一个普通对象,将全部引用该对象的变量关系相应设置为null,便能等待垃圾回收机制GC回收。

image.png

map

  • 数据结构Map,用于存储映射关系。

    • 事实上我们对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);
    • 某些情况下我们可能希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key;对象的key内部会自动toString();
  • Map常见的属性和方法

    • size 返回Set中元素的个数。[属性]
    • set(key, value):在Map中添加key、value,并且返回整个Map对象。
    • get(key):根据key获取Map中的value。
    • has(value) 判断某个元素是否存在,返回boolean类型。
    • clear() 清空所有元素,没有返回值。
    • forEach(value,key,map) 遍历
  • 使用场景

    • 缓存
    const decorator = (fn) => {
      const mapList = new Map();
      return (x, y) => {
        const sum = `${x}${y}`;
        if (mapList.has(sum)) {
          return mapList.get(sum);
        }
        const res = fn(x, y)
        mapList.set(sum, res);
        return res;
      }
    }

    let work = (a, b) => {
      return a + b;
    }

    work = decorator(work)
    work(1, 2);
    work(1, 2); // 这里实际上用的缓存
    work(3, 4);
    work(3, 4); // 这里实际上用的缓存
  • 测试
    // 对比Object,JavaScript对象的key只能是字符串或者是ES6新增的Symbol
    const obj = { name: "why" };
    const obj1 = { name: "coderwhy" };

    const info = {
      [obj]: "zhangsan",
      [obj1]: "lisi"
    };
    console.log(obj); //我们会发现key 被自动toString了,而且也会被覆盖。


    // 使用map作为key
    var map = new Map();
    map.set(obj, "zhangsan"); // 返回整个Map对象
    map.set(obj1, "lisi");  // 返回整个Map对象
    map.set("1", 2); // 返回整个Map对象
    map.set("3", 4); // 返回整个Map对象
    // 通过key 获取 value
    map.get(obj);        // 返回key 对应的 value
    // 获取长度
    console.log(map.size); // 返回长度
    // 通过key 删除
    map.delete(obj); // 返回boolean
    // 判断key 是否存在
    map.has(obj1); // 返回boolean


    // forEach 遍历map
    map.forEach((value, key, map) => {
      console.log(value, key, map);
    })

    // for...of 遍历
    // 结构出来
    for (const [key, value] of map) {
      console.log(key, value);
    }

    // for...of 遍历
    // 不解构
    for (const item of map) {
      // 0对应key 1对应value
      console.log(item[0], item[1]);
    }
    
    

WeakMap

  • WeakMap的key只能使用对象,不接受其他的类型作为key

  • WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;

  • WeakMap常见方法:

    • set(key, value):在Map中添加key、value,并且返回整个Map对象
    • get(key):根据key获取Map中的value
    • has(key):判断是否包括某一个key,返回Boolean类型
    • delete(key):根据key删除一个键值对,返回Boolean类型
  • Weak使用场景(vue3响应式原理)

    const obj1 = {
      name: "why",
      age: 18
    }

    const obj2 = {
      name: "why",
      age: 18
    }

    // 1.创建WeakMap
    const weakMap = new WeakMap();

    // 2. 收集依赖结构
    // 2.1 使用map来收集
    const obj1Map = new Map();
    obj1Map.set("name", [obj1GetName, obj1SetName])
    obj1Map.set("age", [obj1GetAge, obj1SetAge])
    weakMap.set(obj1, obj1Map)


    // 3.如果obj1.name发生改变
    // Proxy/Object.defineProperty
    obj1.name = "test";
    const targetMap = weakMap.get(obj1);
    const fns = targetMap.get("name")
    fns.forEach(item => item())


    function obj1GetName() {
      console.log("obj1GetName")
    }

    function obj1SetName() {
      console.log("obj1SetName")
    }

    function obj1GetAge() {
      console.log("obj1GetAge")
    }

    function obj1SetAge() {
      console.log("obj1SetAge")
    }