一、Monkey Patching(猴子补丁)的内存与原型链机制
Monkey Patching(猴子补丁) 是运行时动态修改已有对象 / 类 / 模块的属性、方法的技术,不修改源码,仅在内存中改写原生 / 第三方代码的行为。
在 JavaScript 中,它的核心底层机制完全依赖 JS 原型链(Prototype Chain) 和 堆内存的动态修改特性。
基础补充:
- 内存模型
- JS 中对象 / 函数 / 原型都存在堆内存 ,变量只是引用(内存地址) 。
- 原型链的核心原则
- 实例 → 构造函数.prototype → Object.prototype → null
- 读取属性 / 方法时,JS 会沿着原型链向上查找
- 修改原型对象 = 所有实例立刻生效(因为共享内存)
正确的猴子补丁写法:
js
// 安全模板:备份 + 调用原生 + 不破坏原有功能
(function safeMonkeyPatch() {
// 备份原生
const original = Array.prototype.push; Array.prototype.push = function(...args) {
// 你的自定义逻辑
// ...
// 严格调用原生方法
return original.apply(this, args);
};
})();
二、RequestIdleCallback 的时间切片与 Frame 渲染生命周期
我们先来了解两个概念:
(1)requestIdleCallback(rIC)
浏览器提供的、在当前帧渲染完成后、下一帧开始前 的主线程空闲时段,执行低优先级、可中断任务的 API。
- 一帧≈16.67ms(60fps)
(2)requestAnimationFrame(rAF)
在帧渲染前执行(高优,对齐视觉)
浏览器一帧生命周期(主线程)
-
输入事件处理: 触摸、点击、滚轮事件响应。
-
定时器: 触发
setTimeout/setInterval。 -
开始帧: 触发
requestAnimationFrame(rAF)。 -
样式与布局: Recalculate Style(重新计算样式)、Layout(重排)。
-
绘制: Paint(重绘)、Composite(合成分层提交给 GPU)。
-
空闲期(Idle Period): 此时
requestIdleCallback登场。

底层截止时间机制(Deadline)
V8 会向 rIC 的回调函数传入一个 IdleDeadline 对象。
deadline.timeRemaining()的底层原理是:浏览器内核计算当前帧剩余的绝对时间(当前时间戳减去当前帧期望结束的时间戳)。- 如果前面 Layout 和 Paint 耗时太长(比如 3D 渲染开销大),剩余时间就是
0,rIC 的回调就会被推迟到下一帧甚至更后面的空闲期执行。 - 致命缺陷: rIC 极其不稳定。在 2026 年的高阶前端开发中,React 18 之后已经彻底废弃了原生 rIC,改用自研的
Scheduler,通过MessageChannel模拟宏任务来精确控制时间切片(Time Slicing)。
用法:
scss
const id=requestIdleCallback(
(deadline) => {
// 空闲回调:在渲染后执行
while (deadline.timeRemaining() > 1) {
// 时间切片:剩>1ms就继续
doOneUnitWork(); // 做一小片任务(<1ms)
}
if (hasMoreWork) requestIdleCallback(work); // 下次空闲继续 },
{ timeout: 1000 } // 最长等1s,超时强制执行
);
三、Beacon API 与 Navigator.sendBeacon 的非阻塞原子传输
Beacon API 是浏览器提供的轻量、异步、非阻塞、可靠 的数据上报接口,核心方法为 navigator.sendBeacon(url, data) 。
它专门解决 页面卸载(unload/pagehide)时,异步请求被浏览器丢弃、同步请求阻塞导航 的痛点。
它的底层机制是异步、低优先级、不阻塞卸载。
关键能力
- 非阻塞:不阻塞主线程、不延迟页面卸载 / 下一页加载。
- 原子可靠 :浏览器保证请求发起并尽量完成,页面销毁后仍可在后台跑完。
- 低优先级、独立调度 :不抢关键资源,适合埋点 / 日志 / 统计等无需响应的上报。
为什么需要 Beacon
在 sendBeacon 之前,页面卸载时发数据常用三种方式,都有严重问题:
-
同步 XHR(最常见)
js// 阻塞主线程,卡死卸载,下一页变慢 const xhr=new XMLHttpRequest(); xhr.open('POST', '/log', false); // 同步 xhr.send(data);问题:阻塞页面卸载,用户必须等请求完成才能跳转,体验极差。
-
异步 XHR(不可靠)
js// unload 里的异步 XHR 会被浏览器直接丢弃 window.addEventListener('unload', () => { const xhr=new XMLHttpRequest(); xhr.open('POST', '/log'); xhr.send(data); // 大概率发不出去 });问题:浏览器会终止未完成的异步请求,数据丢失严重。
sendBeacon 的"离体运行"机制
navigator.sendBeacon(url, data) 的底层核心在于跨进程调度:
- 任务移交: 当你在 JS 中调用它时,渲染进程并不会自己去发请求,而是将请求的 URL 和数据(通常是
Blob或FormData)序列化后,打包发送给浏览器的 网络进程(Network Process) 。 - 异步脱离: 任务一旦移交成功,
sendBeacon立即返回true。此时,即使渲染进程被销毁(网页关闭),网络进程依然常驻在操作系统中,它会默默地在后台把这个网络请求发完,不受页面生命周期的限制。 - 原子性与限制: 它采用
POST方法,不需要OPTIONS预检请求(因为大都是简单请求格式),且上报的数据有严格的体积限制(通常不能超过 64KB),这就是为什么你在简历里写"批量压缩上报"切中了大厂痛点。
sendBeacon 是浏览器通过 keepalive 标志实现的、脱离主线程调度的非阻塞原子上报机制:调用即入队、不阻塞卸载、页面销毁后仍能后台完成发送,是埋点日志类数据上报的最优解。
四、Web Worker 的多线程与结构化克隆
这是前端真正的多线程 、内存隔离 、通信机制的核心知识点。
1. Web Worker 是什么?
JS 主线程(渲染 + 交互)开辟一个独立的后台子线程,只跑计算,不碰 DOM,不阻塞渲染。
核心三要素
- 多线程并行 :主线程 + Worker 线程 同时运行
- 内存完全隔离 :两个线程不共享内存、对象、变量
- 只能通过消息通信 :用
postMessage+onmessage传递数据
2. 浏览器线程模型
浏览器是多进程、多线程 的。每个标签页(渲染进程)都有一个主线程 (负责 JS 执行、DOM 构建、渲染布局)。Web Worker 的底层是浏览器向操作系统申请的一条独立的操作系统级线程(OS Thread) 。
- Worker 线程拥有自己独立的 V8 实例(Isolate)和上下文(Context)。
- 无法操作 DOM: 因为浏览器的渲染引擎和 DOM 树不是线程安全的(Thread-Safe),多线程同时修改 DOM 会导致渲染崩溃,所以 Worker 被设计为无法访问
window和document。
3. 多线程机制
1. 主线程 vs Worker 线程
- 主线程:负责 DOM、渲染、rAF、事件、UI
- Worker 线程 :负责纯计算(加密、解析、大数据处理、长任务)
2. 隔离规则(必须记住)
- ✅ Worker 可以:计算、网络请求、定时器、IndexedDB
- ❌ Worker 不可以:访问 window、document、DOM、样式、渲染
- ❌ Worker 不共享任何变量、对象、内存
4. 通信机制:结构化克隆算法
本质:不能共享对象,只能复制对象
结构化克隆是浏览器内部的深拷贝算法
-
可以复制:
- 对象、数组
- 字符串、数字、布尔、null
- Date、RegExp
- Blob、File、FileList
- ArrayBuffer、TypedArray
- Map、Set
-
不可以复制:
- Function(函数不能传)
- DOM 对象
- 原型链
- Symbol
内存机制
-
postMessage(data)→ 触发结构化克隆 → 生成一份全新内存副本→ 发送给对方线程 -
两个线程拥有完全独立的副本
-
修改对方不会影响自己