JS Mock底层原理是接管调用路径:Proxy拦截对象行为(如fetch调用、属性读写),重写全局方法或模块导出以替换真实逻辑,二者协同实现细粒度控制与入口级劫持。

JS 中 Mock 的底层原理,核心就两点:用 Proxy 拦截对象行为,再用 重写关键方法或全局变量来替换真实逻辑。它不是“伪造数据”,而是“接管调用路径”——让代码以为在跟真实接口、真实函数打交道,实际背后全是模拟的响应和逻辑。
Proxy 是怎么当“中间人”的
Mock 工具(比如 Mock.js、msw、或者手写的 mock 逻辑)常把目标对象(如 fetch、axios、或某个 service 类)包进 Proxy 里:
- 当你写
fetch('/api/user'),Proxy 的applytrap 就被触发,直接拦下这次调用,不发真实网络请求 - 当你读
user.name,Proxy 的gettrap 可以返回预设的假值,甚至动态生成(比如每次返回不同 ID) - 当你改
config.timeout = 5000,settrap 可以记录修改、校验合法性,或干脆忽略
重写(override)是更直接的“掉包”
有些场景 Proxy 不够用(比如要 mock 全局函数、原生 API、或模块顶层导出),就得靠重写:
- 把
window.fetch原函数先存起来,再赋值成你自己的模拟函数 - 用
jest.mock()替换整个模块导出,让import api from './api'实际导入的是 mock 版本 - 在 Node.js 里重写
require.cache或用vm模块动态执行带 mock 的代码
为什么两者经常一起用
单一手段有局限:
- 只靠重写:难做细粒度控制(比如只 mock 某次特定 URL 的 fetch,其余照常)
- 只靠 Proxy:无法拦截未被代理的原始调用(比如直接调
XMLHttpRequest,没包 Proxy 就管不到) - 配合使用:Proxy 管“对象内部行为”,重写管“入口级调用”,一层兜底一层增强
打个生活比方
想象你在点外卖:
- Proxy 就像智能客服:你打电话说“我要一份宫保鸡丁”,客服听到后不转给后厨,而是查菜单、看库存、按规则返回“已下单”或“缺辣椒,换成青椒?”,全程你感觉还是在跟餐厅通话
- 重写就像临时换老板:你打开外卖 App,App 本身被替换成测试版——首页所有按钮都连到模拟厨房,连支付都跳过支付宝,直通“订单成功”页。你根本不知道后端换了人











