前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[科普]ES6一些不常见的小知识

[科普]ES6一些不常见的小知识

作者头像
前端LeBron
发布2021-12-08 16:41:13
3560
发布2021-12-08 16:41:13
举报
文章被收录于专栏:前端LeBron

写作不易,未经作者允许禁止以任何形式转载!

WeakMap

前置知识[深入浅出]JavaScript GC 垃圾回收机制

什么是WeakMap?

WeakMap是key / value的组合,key只接受对象,不接受基本类型,value可以为任意类型。

方法

  • set(key, value)

在WeakMap中设置一组关联对象,返回WeakMap对象

  • get(key)

返回key的关联对象,不存在时返回undefined

  • has(key)

根据是否有key关联对象,放回一个Boolean值

  • delete(key)

移除key的关联对象,之后执行has(key)方法返回false

和Map有什么区别?

  1. Map的key / value,都可以是任意类型
  • WeakMap的key只能是对象,value可以是任意类型
代码语言:javascript
复制
const name = "LeBron";
const person = {
  name: "LeBron",
  age: 21,
};

let wk = new WeakMap();

wk.set(person, "nice");
console.log(wk.get(person)); // nice
wk.set(name, 1); // TypeError: Invalid value used as weak map key

let map = new Map();
map.set(name, "JS");
map.set(person, "nice");
console.log(map.get(name)); // JS
console.log(map.get(person)); // nice
  1. Map的key / value 是可遍历的,因为它的 key / value 存放在一个数组中。
  • WeakMap不存在存放 key / value 的数组,所以不可遍历。
代码语言:javascript
复制
const name = "LeBron";
const person = {
  name: "LeBron",
  age: 21,
};

let wk = new WeakMap();
wk.set(name, "JS");
wk.set(person, "nice");
console.log(wk.keys()); // TypeError: wk.keys is not a function
console.log(wk.values()); // TypeError: wk.values is not a function
console.log(wk.entries());  // TypeError: wk.entries is not a function

let map = new Map();
map.set(person, "nice");
console.log(map.keys()); // [Map Iterator] { 'LeBron', { name: 'LeBron', age: 21 } }
console.log(map.values()); // [Map Iterator] { 'JS', 'nice' }
console.log(map.entries()); // [Map Entries] {
                           //  [ 'LeBron', 'JS' ],
                           //  [ { name: 'LeBron', age: 21 }, 'nice' ]
                          //  }
  1. Map对键进行强引用,即使键被标记为null了,由于Map的key / value数组还在引用,key不会被GC。
  • WeakMap对key进行弱引用,在key被标记为null了以后。由于是弱引用,也不存在key / value数组引用,不影响key的GC。
  • 以下程序需要手动GC 启动方法:node --expose-gc xxx

Map

代码语言:javascript
复制
function memmorySizeLogger() {
  global.gc();
  const used = process.memoryUsage().heapUsed;
  console.log((used / 1024 / 1024).toFixed(2) + "M");
}

memmorySizeLogger();  // 1.79M

let person = {
  name: "LeBron",
  age: 21,
  tmp: new Array(5 * 1024 * 1024),
};

memmorySizeLogger(); // 41.96M

let map = new Map();
memmorySizeLogger(); // 41.96M

map.set(person, "nice");
memmorySizeLogger(); // 41.96M

person = null;
memmorySizeLogger();  // 41.96M  person的内存没有被回收

如果想在这种情况下正常GC,标记为null前需先执行map.delete(person)

WeakMap

代码语言:javascript
复制
function memmorySizeLogger() {
  global.gc();
  const used = process.memoryUsage().heapUsed;
  console.log((used / 1024 / 1024).toFixed(2) + "M");
}

memmorySizeLogger();  // 1.79M

let person = {
  name: "LeBron",
  age: 21,
  tmp: new Array(5 * 1024 * 1024),
};

memmorySizeLogger(); // 41.96M

let wk = new WeakMap();
memmorySizeLogger();  // 41.96M

wk.set(person, "nice");
memmorySizeLogger();  // 41.96M

person = null;
memmorySizeLogger();  // 1.96M  person的内存被回收

Why WeakMap?

  • 在JS里的Map API共用两个数组(key、value),设置的key、value都会加到这两个数组的末尾,并对key产生引用。当从map取值时,需要遍历所有的key,然后通过索引从value数组中取出相应index的值。
    • 缺点一:
      • 赋值、搜索都是O(n)复杂度
    • 缺点二:
      • 使用Map容易出现内存泄漏,因为数组一直引用着每个key和value,导致无法正常GC。
  • WeakMap对key进行弱引用,不影响正常GC
    • key被GC后失效
  • 如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap
    • 如果需要遍历 / 迭代,则需要使用Map

应用场景

保存DOM节点数据

代码语言:javascript
复制
let domData = new WeakMap();

let dom = document.getElementById("xxx");

const anyDomData = getDomData(dom);
domData.set(dom, anyDomData);
console.log(domData.get(dom)); 

dom.parentNode.removeChild(dom);
dom = null;

缓存相关数据

代码语言:javascript
复制
let cache = new WeakMap();

class HandleCache {
  get(key) {
    if (cache.has(key)) {
      return cache.get(key);
    } else {
      return undefined;
    }
  }
  set(key, value) {
    cache.set(key, value)
  }
  delete(key){
    cache.delete(key)
  }
}

封装私有属性

代码语言:javascript
复制
let privateData = new WeakMap();

class Person{
  constructor(name, age){
    privateData.set(this,{name, age});
  }
  getData(){
    return privateData.get(this);
  }
}

WeakSet

前置知识[深入浅出]JavaScript GC 垃圾回收机制

什么是WeakSet

WeakSet对象是一些对象值的集合,并且其中的每个对象只能出现一次,在WeakSet集合中是唯一的

方法

  • add(value)

在该WeakSet对象中添加一个新的元素value

  • delete(value)

在该WeakSet对象中删除value这个元素后,has方法会返回false。

  • has(value)

返回一个Boolean值,表示给定的value值是否存在这个WeakSet中

和Set有什么区别

  1. Set的value可以是任何值,WeakSet的值只能是对象
代码语言:javascript
复制
const name = "LeBron";
const age = 21;
const person = {
  name: "LeBron",
  age: 21,
};

const ws = new WeakSet();
const set = new Set();

set.add(name);
set.add(age);
set.add(person); 

ws.add(person);
ws.add(name); // TypeError: Invalid value used in weak set
ws.add(age);    //  TypeError: Invalid value used in weak set
  1. Set是可遍历的,WeakSet不可遍历
  • Set存在一个数组存放value的值,引用原对象,故可遍历
  • WeakSet不存这样的数组,故不可遍历
代码语言:javascript
复制
const name = "LeBron";
const age = 21;
const person = {
  name: "LeBron",
  age: 21,
};

const ws = new WeakSet();
const set = new Set();

set.add(name);
set.add(age);
set.add(person);
console.log(set.values()); // { 'LeBron', 21, { name: 'LeBron', age: 21 } }

ws.add(person);
ws.add(name); 
ws.add(age);   
console.log(set.values());  // TypeError: ws.values is not a function
  1. Set影响GC,而WeakSet不影响
  • 以下程序需要手动GC 启动方法:node --expose-gc xxx

Set存在values数组,在原value指向null后,values数组仍对value的值存在强引用,影响正常GC

代码语言:javascript
复制
function memmorySizeLogger() {
  global.gc();
  const used = process.memoryUsage().heapUsed;
  console.log((used / 1024 / 1024).toFixed(2) + "M");
}

memmorySizeLogger(); // 1.79M

let person = {
  name: "LeBron",
  age: 21,
  tmp: new Array(5 * 1024 * 1024),
};

memmorySizeLogger(); // 41.96M

const set = new Set();
set.add(person);

memmorySizeLogger(); // 41.96M

person = null;

memmorySizeLogger();  // 41.96M

WeakSet不存在这样的数组,故不影响正常GC

代码语言:javascript
复制
function memmorySizeLogger() {
  global.gc();
  const used = process.memoryUsage().heapUsed;
  console.log((used / 1024 / 1024).toFixed(2) + "M");
}

memmorySizeLogger(); // 1.79M

let person = {
  name: "LeBron",
  age: 21,
  tmp: new Array(5 * 1024 * 1024),
};

memmorySizeLogger(); // 41.96M

const ws = new WeakSet();
ws.add(person);

memmorySizeLogger();  // 41.96M

person = null;

memmorySizeLogger();  // 1.96M

应用场景

检测循环引用

递归调用自身的函数需要一种通过跟踪哪些对象已被处理,来应对循环数据结构的方法

代码语言:javascript
复制
// 对 传入的subject对象 内部存储的所有内容执行回调
function execRecursively(fn, subject, _refs = null){
        if(!_refs)
                _refs = new WeakSet();

        // 避免无限递归
        if(_refs.has(subject))
                return;

        fn(subject);
        if("object" === typeof subject){
                _refs.add(subject);
                for(let key in subject)
                        execRecursively(fn, subject[key], _refs);
        }
}

const foo = {
        foo: "Foo",
        bar: {
                bar: "Bar"
        }
};

foo.bar.baz = foo; // 循环引用!
execRecursively(obj => console.log(obj), foo);

Reflect

Reflect译为反射,是一个内置的新的全局对象,它提供拦截JavaScript操作的方法。这些方法与Proxy handler的方法相同。Reflect不是一个函数对象,是静态的类似工具函数,类似Math,因此它是不可构造的

Reflect的静态方法

具体用法参考:Reflect MDN文档

  • Reflect.apply()
  • Reflect.construct()
  • Reflect.defineProperty()
  • Reflect.deleteProperty()
  • Reflect.get()
  • Reflect.getOwnPropertyDescriptor()
  • Reflect.getPrototypeOf()
  • Reflect.has()
  • Reflect.isExtensible()
  • Reflect.ownKeys()
  • Reflect.preventExtensions()
  • Reflect.set()
  • Reflect.setPrototypeOf()

这些方法与Proxy handler的方法的命名相同,其中的一些方法与Object的方法相同,尽管二者之间存在着某些细微的差别

  • 有什么不同?
    • Reflect的静态方法进行相应操作会返回一个Boolean值
      • 操作成功返回true
      • 操作失败返回false
    • 将常规的命令式操作转换成了函数式操作,编程方式增加了元编程。
      • 例如delete、赋值、判断等
    • 命令式操作失败一般会报错,而Reflect不会,返回一个Boolean值判断是否成功。
  • 内置对象中已经存在了一些反射API,Reflect将他们聚合起来并进行了优化

什么是元编程?

  • 元编程即对编程语言进行编程
  • 例如Proxy对象可以进行代理,拦截get、set操作
  • 而在程序中获取的是你编程后的值。
  • Reflect就是一种反射,调用的是处理过后的各内置对象上的方法
    • 所以各内置对象的方法改变后,Reflect调用的方法也是改变了的
    • 类似于封装了一层

Reflect的优点

  1. 优化命名空间

你会发现JS的内置反射方法散落在各处,Reflect将他们很好地组织了起来。

  1. 增强代码的健壮性

使用Reflect进行操作不容易抛出异常、线程阻塞,使代码更健壮地运行。

  1. 为什么不直接挂在Object上?
  • 反射的对象不仅针对于Object,还可能针对函数
    • 例如apply,调用Object.apply(myFunc)还是挺奇怪的
  • 用一个单一的对象保存内置方法能够保证JavaScript代码其他对象的纯净性
    • 这样要优于直接反射挂载到构造函数或者原形上
    • 更优于直接使用全局变量,这样JS关键字将越来越多。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端LeBron 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • WeakMap
    • 什么是WeakMap?
      • 方法
        • 和Map有什么区别?
          • Why WeakMap?
            • 应用场景
              • 保存DOM节点数据
              • 缓存相关数据
              • 封装私有属性
          • WeakSet
            • 什么是WeakSet
              • 方法
                • 和Set有什么区别
                  • 应用场景
                    • 检测循环引用
                • Reflect
                  • Reflect的静态方法
                    • 什么是元编程?
                      • Reflect的优点
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档