寫做不易,未經做者容許禁止以任何形式轉載!
若是以爲文章不錯,歡迎關注、點贊和分享!
持續分享技術博文,關注微信公衆號 👉🏻 前端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關鍵字將愈來愈多。