JavaScript Proxies: 如何在数组排序后仅触发一次通知

6次阅读

JavaScript Proxies: 如何在数组排序后仅触发一次通知

本文介绍如何绕过 proxy 对数组排序时的多次拦截问题,通过方法修饰器(around)实现排序完成后的单次统一通知,避免原生 proxysort 过程中因内部索引赋值而频繁触发 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 拦截——它语义清晰、可控性强,且天然支持“操作完成即通知”的聚合场景。

text=ZqhQzanResources