
本文介绍如何绕过 proxy 对数组排序时的多次拦截问题,通过方法修饰器(around)实现排序完成后的单次统一通知,避免原生 proxy 在 sort 过程中因内部索引赋值而频繁触发 set 拦截。
在 JavaScript 中,使用 Proxy 拦截数组的 sort() 方法看似直观,但实际运行会发现:sort() 内部会多次修改数组元素(如交换位置),导致 handler.set 被反复调用——正如示例中两次输出 "0" 1 和 "0" 2。这是因为 sort() 并非原子操作,而是通过多次 target[index] = value 实现重排,Proxy 无法感知“排序开始/结束”的语义边界。
因此,直接依赖 Proxy 的 set 拦截无法优雅解决“聚合通知”需求。更可靠的方式是不拦截底层赋值,而是增强(wrap)sort 方法本身,即采用面向切面编程(AOP)思想中的 around 方法修饰器。
around 修饰器接收三个参数:原始方法(proceed)、自定义处理逻辑(handler)和目标对象(target),返回一个新函数。该函数在执行时先调用 handler,由其决定是否、何时以及如何调用原始方法,并在其前后插入任意逻辑(如日志、状态更新、统一通知等)。
以下是完整可运行的实现:
立即学习“Java免费学习笔记(深入)”;
// around 修饰器:通用方法增强工具
function around(proceed, handler, target) {
return function (...args) {
return handler.call(target ?? null, proceed, handler, args);
};
}
// 自定义 handler:排序完成后发出单次通知
function notifyAboutFinishedTask(proceed, handler, args) {
const arr = this;
// ✅ 执行原始 sort(支持无参或传入 compareFn)
proceed.apply(arr, args);
// ✅ 排序完成,仅触发一次通知
console.log('✅ array sorted');
console.log('→ result:', arr);
console.log('→ sort arguments:', args.length ? args : '(none)');
}
// 使用示例
const arr = [2, 1];
console.log('? initial:', arr);
// 替换当前数组实例的 sort 方法(非污染 Array.prototype)
arr.sort = around(arr.sort, notifyAboutFinishedTask, arr);
// 测试两种调用方式
arr.sort(); // → 升序
arr.sort((a, b) => b - a); // → 降序⚠️ 注意事项:此方案不修改全局 Array.prototype.sort,仅作用于特定数组实例,避免副作用;若需批量应用,可封装为工具函数(如 enhanceSort(arr, callback));around 本质是函数式增强,与 Proxy 互补而非替代:Proxy 适合细粒度数据访问控制,around 更适合行为级横切关注点(如审计、性能埋点、状态同步);对于 Vue/React 等响应式框架,若需同步视图更新,请确保在通知逻辑中触发相应响应式机制(如 Vue.set 或 setState)。
综上,当面对 sort、reverse、fill 等批量变更数组的方法时,优先考虑方法增强而非 Proxy 拦截——它语义清晰、可控性强,且天然支持“操作完成即通知”的聚合场景。










