写做不易,未经做者容许禁止以任何形式转载!
若是以为文章不错,欢迎关注、点赞和分享!
持续分享技术博文,关注微信公众号 👉🏻 前端LeBron前端
前置知识[深刻浅出]JavaScript GC 垃圾回收机制 node
WeakMap是key / value的组合,key只接受对象,不接受基本类型,value能够为任意类型。web
在WeakMap中设置一组关联对象,返回WeakMap对象编程
返回key的关联对象,不存在时返回undefined数组
根据是否有key关联对象,放回一个Boolean值缓存
移除key的关联对象,以后执行has(key)方法返回false微信
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
复制代码
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' ]
// }
复制代码
WeakMap对key进行弱引用,在key被标记为null了之后。因为是弱引用,也不存在key / value数组引用,不影响key的GC。markdown
如下程序须要手动GC 启动方法:node --expose-gc xxx
数据结构
Mapapp
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
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的内存被回收
复制代码
赋值、搜索都是O(n)复杂度
使用Map容易出现内存泄漏,由于数组一直引用着每一个key和value,致使没法正常GC。
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;
复制代码
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)
}
}
复制代码
let privateData = new WeakMap();
class Person{
constructor(name, age){
privateData.set(this,{name, age});
}
getData(){
return privateData.get(this);
}
}
复制代码
WeakSet对象是一些对象值的集合,而且其中的每一个对象只能出现一次,在WeakSet集合中是惟一的
在该WeakSet对象中添加一个新的元素value
在该WeakSet对象中删除value这个元素后,has方法会返回false。
返回一个Boolean值,表示给定的value值是否存在这个WeakSet中
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
复制代码
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
复制代码
node --expose-gc xxx
Set存在values数组,在原value指向null后,values数组仍对value的值存在强引用,影响正常GC
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
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
复制代码
递归调用自身的函数须要一种经过跟踪哪些对象已被处理,来应对循环数据结构的方法
// 对 传入的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译为反射,是一个内置的新的全局对象,它提供拦截JavaScript操做的方法。这些方法与Proxy handler的方法相同。Reflect不是一个函数对象,是静态的相似工具函数,相似Math,所以它是不可构造的
具体用法参考: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的方法相同,尽管两者之间存在着某些细微的差异
你会发现JS的内置反射方法散落在各处,Reflect将他们很好地组织了起来。
使用Reflect进行操做不容易抛出异常、线程阻塞,使代码更健壮地运行。
这样要优于直接反射挂载到构造函数或者原形上
更优于直接使用全局变量,这样JS关键字将愈来愈多。