理解垃圾回收器的工作原理,使用弱引用来避免泄漏,并用防抖和节流来平滑开销较大的工作。
在 Kodokon 中打开本课V8 的垃圾回收器基于可达性(reachability):一旦没有任何引用链把某个对象连接到根(栈、活动的作用域、全局变量),它就变得可被回收。GC 是分代的:年轻对象生活在*新生代(nursery)*中,会被频繁而快速地回收(scavenge);幸存者会被晋升到老生代,由增量式的标记-清除(mark-and-sweep)处理。经典的泄漏并不来自 GC,而来自你忘记的引用:基于 Map 的缓存、从不解绑的监听器、捕获了大对象的闭包。
const strongCache = new Map();
const weakCache = new WeakMap();
let session = { user: "ada" };
strongCache.set(session, "data");
weakCache.set(session, "data");
session = null;
// Map: the key retains the object -> memory leak
// WeakMap: the entry becomes collectableWeakMap 不会保留它的键:当一个键从其他任何地方都变得不可达时,整个条目就会消失。由此带来的设计后果:键必须是对象(或未注册的 symbol),而且这种结构既不可迭代也不可测量——暴露它的 size 会泄露 GC 的行为,而 GC 是非确定性的。WeakRef 更进一步:它给你一个直接的弱引用,需要用 deref() 来解引用。
let config = { theme: "dark" };
const ref = new WeakRef(config);
function readTheme() {
const target = ref.deref();
return target ? target.theme : "default";
}
console.log(readTheme()); // "dark"
config = null;
// After a GC pass, deref() may
// return undefined: plan a fallback.在感知性能一侧,高频事件(scroll、resize、input、mousemove)每秒可能触发数百次调用。有两种互补的策略:防抖(debounce)只在一段静默期之后才运行函数(非常适合输入即搜索),而节流(throttle)则保证每个时间间隔内至多执行一次(非常适合跟踪滚动)。
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(
() => fn.apply(this, args),
delay
);
};
}function throttle(fn, interval) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn.apply(this, args);
}
};
}ref.deref() 可能返回什么?undefinednull