Proxy无法直接拦截数组遍历操作,需手动重写Symbol.iterator以拦截for...of等;forEach等高阶函数则需在get中劫持方法本身。

Proxy 本身无法直接拦截数组的遍历操作(如 for...of、forEach、map 等),因为这些操作依赖的是数组的迭代器(即 [Symbol.iterator] 方法),而 Proxy 只能拦截对象自身的属性访问和方法调用,不能自动重写内置迭代行为——除非你手动覆盖 Symbol.iterator。
拦截 for...of 和展开运算符的关键:重写 Symbol.iterator
数组的遍历行为由其 [Symbol.iterator] 方法控制。要拦截遍历,必须在 Proxy 的 get 拦截器中对这个 symbol 属性做特殊处理,返回自定义迭代器。
- 原生数组的
[Symbol.iterator]返回一个迭代器对象(有next()方法) - Proxy 中需在
get里识别Symbol.iterator,并返回包装后的迭代器 - 自定义迭代器可添加日志、过滤、转换等逻辑
示例:记录每次 for...of 遍历时的访问
const arr = [1, 2, 3];
const proxied = new Proxy(arr, {
get(target, prop, receiver) {
// 拦截 Symbol.iterator 调用
if (prop === Symbol.iterator) {
return function* () {
console.log('开始遍历数组');
for (const item of target) {
console.log('当前项:', item);
yield item;
}
console.log('遍历结束');
};
}
return Reflect.get(target, prop, receiver);
}
});
<p>for (const x of proxied) {
// 输出:开始遍历数组 → 当前项: 1 → 当前项: 2 → 当前项: 3 → 遍历结束
}
注意 forEach/map/filter 等高阶函数无法被 Proxy 直接拦截
这些方法是数组原型上的函数,调用时 this 指向原数组,Proxy 不会介入其内部实现。即使你代理了数组,arr.forEach(...) 仍走原生逻辑,不会触发你的 get 或 apply。
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 想拦截
forEach,需在get中对'forEach'属性返回包装后的新函数 - 同理适用于
map、filter、reduce等 - 本质是“函数劫持”,不是“遍历劫持”
示例:拦截 forEach
const proxied = new Proxy(arr, {
get(target, prop, receiver) {
if (prop === 'forEach') {
return function(callback, thisArg) {
console.log('forEach 被调用');
// 手动遍历并执行 callback(可加逻辑)
for (let i = 0; i < target.length; i++) {
callback.call(thisArg, target[i], i, target);
}
};
}
if (prop === Symbol.iterator) {
// 同上,自定义迭代器...
}
return Reflect.get(target, prop, receiver);
}
});
Proxy 无法拦截的常见遍历场景
以下操作不会触发 Proxy 的任何 trap,因为它们不访问数组对象的属性或方法,而是直接读取内存或调用引擎底层机制:
for (let i = 0; i —— 仅读 <code>length和索引属性,可被get拦截,但循环本身无感知-
arr[0]、arr.length—— 这些可被get拦截,但不属于“遍历操作” - JSON.stringify(arr) —— 序列化走内部算法,绕过 Proxy
- Array.from(arr) —— 内部调用
arr[Symbol.iterator],所以如果你已重写该 symbol,它会被拦截
更实用的替代思路:封装成可观察数组类
如果目标是全面监控数组使用行为(尤其在响应式系统中),单纯靠 Proxy 拦截遍历并不健壮。推荐结合:
- 用 Proxy 拦截
push/pop/length等变更操作 - 显式提供
observeEach()、mapObserved()等受控方法 - 或使用
Array.prototype上的代理方法(不推荐污染原型) - 现代方案如 Vue 3 的
reactive([])就是基于 Proxy + 迭代器重写 + 副作用追踪的组合
不复杂但容易忽略:真正“拦截遍历”的核心,永远是掌控 Symbol.iterator,而不是期待 Proxy 自动理解 for...of 的语义。

















