恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
eslint-plugin-unicorn 的 no-late-current-target-access 规则:禁止在事件派发结束后访问 `event.currentTarget`
首页
资讯中心
/
eslint-plugin-unicorn 的 no-late-current-target-access 规则:禁止在事件派发结束后访问 `event.currentTarget`
eslint-plugin-unicorn 的 no-late-current-target-access 规则:禁止在事件派发结束后访问 `event.currentTarget`
发布时间:2026/9/18 23:47:36
eslint-plugin-unicorn 的 no-late-current-target-access 规则禁止在事件派发结束后访问event.currentTarget【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicornno-late-current-target-access是 eslint-plugin-unicorn 内置的一条problem类型规则用于在静态分析阶段捕获对event.currentTarget的过期访问——即事件处理器在await、yield或setTimeout()、.then()等延迟回调中读取currentTarget的场景。本指南以 docs/rules/no-late-current-target-access.md 为主体结合 规则源码、共享检测器 与 测试用例完整讲解该问题的成因、规则的检测逻辑、修复方式与已知局限帮助你在浏览器事件处理代码中避免读到null的隐性 bug。问题本质currentTarget只在同步派发期间有效根据 Web API 规范Event.currentTarget标识当前正在处理事件的元素但它只在事件被同步派发的过程中被赋值。一旦事件处理器让出执行权——例如await一个 Promise、在生成器里yield或者把回调交给setTimeout()/Promise.prototype.then()延迟执行——currentTarget就会被重置为null。此时再读取它得到的不是预期的 DOM 元素而是null随后调用null.click()、null.disabled之类操作会直接抛出 TypeError 或产生静默失效。这是浏览器事件模型中一个反直觉、但非常真实的陷阱。正确做法是在同步执行阶段把event.currentTarget保存到局部变量之后一律使用该变量因为普通变量不会随事件派发结束而被清空。规则触发场景与修复示例规则文档给出了两组典型反例/正例全部可在项目中直接验证。场景一延迟回调中访问// ❌ 错误setTimeout 回调运行时currentTarget 已经是 null element.addEventListener(click, event { setTimeout(() { event.currentTarget.click(); }); }); // ✅ 正确同步阶段先解构保存 element.addEventListener(click, event { const {currentTarget} event; setTimeout(() { currentTarget.click(); }); });场景二await之后访问// ❌ 错误await 之后 currentTarget 已失效 element.addEventListener(click, async event { event.currentTarget.disabled true; await somePromise; event.currentTarget.disabled false; // 这里读取到 null }); // ✅ 正确 element.addEventListener(click, async event { const {currentTarget} event; currentTarget.disabled true; await somePromise; currentTarget.disabled false; });注意第一段错误代码里await之前的那次event.currentTarget.disabled true是合法的——规则只报告挂起点之后的访问测试用例也专门验证了这一点test/no-late-current-target-access.js。规则如何识别事件参数命名模式 变量解析规则并非对所有xxx.currentTarget一视同仁它通过两层过滤来精确定位事件对象成员表达式形态只匹配MemberExpression且属性名必须是字面量currentTarget对象必须是Identifier规则源码。因此event[currentTarget]计算属性访问、event.target不同属性都不在检测范围内。参数名模式对象名必须匹配共享的正则eventParameterNamePattern——/^(?:e|event|evt|[a-z][\dA-Za-z]*Event)$/urules/shared/late-event-handler.js即e、event、evt或以小写字母开头、以Event结尾的驼峰名如mouseDownEvent。变量必须是函数参数通过findVariable解析作用域确认该标识符的defs中存在Parameter类型的定义规则源码。这排除了三类干扰局部声明的同名变量const event getEvent()不会被误报全局event未解析到参数定义() { event.currentTarget }不会被误报内层作用域的遮蔽参数内层回调自己声明event参数时按内层参数判断。底层实现共享的挂起点跟踪器规则的核心逻辑并不散落在自身源码里而是复用了rules/shared/late-event-handler.js导出的createLateEventHandlerTracker——该共享模块同时服务于no-late-current-target-access与姊妹规则no-late-event-control后者负责preventDefault/stopPropagation等控制方法调用过晚的问题见 rules/no-late-event-control.js。跟踪器的工作机制如下1. 遍历时标记已挂起的函数利用 ESLint 的onExit钩子在退出三类节点时把其所属函数加入suspendedFunctions集合rules/shared/late-event-handler.jsAwaitExpressionawait表达式YieldExpressionyield表达式带await的ForOfStatementfor await...of循环由于这些回调在退出节点时执行后续访问currentTarget时能看到此前已经经过的挂起点——这就是源码顺序敏感的基础。2. 命中条件三层判断规则对每个候选访问节点依次判断规则源码是否在嵌套函数中getEnclosingFunction(node) ! handlerFunction。若是直接按in-nested-function报错——因为无法证明嵌套函数一定在同步阶段执行外层函数是否已挂起lateEventHandlerTracker.isFunctionSuspended(handlerFunction)命中则报after-suspension是否位于含挂起点的循环重复段内isInsideSuspendingLoop(node, handlerFunction)。三种情况都不满足时说明访问发生在同步安全区间不报告。3. 循环场景的专项处理isInsideSuspendingLoop遍历从访问节点到 handler 函数之间的祖先链借助isRepeatedLoopPart判断当前子节点是否为循环的重复执行部分rules/shared/late-event-handler.jsForStatementtest、update、body都会重复执行ForInStatement/ForOfStatement除right只求值一次外的部分都会重复执行DoWhileStatement/WhileStatement整体重复。再结合containsSuspensionPointrules/utils/contains-suspension-point.js判断循环体内是否存在await/yield/for await...of。之所以需要这套机制是因为循环体可能在第一轮同步执行此时currentTarget仍有效但第二轮起就发生在await之后了——静态分析必须把循环体内和循环条件中的访问视为迟早会挂起后执行。值得注意的一个实现细节containsSuspensionPoint在递归时遇到嵌套函数会直接返回false不深入因为嵌套函数中的挂起点属于另一个函数不应影响外层判断rules/utils/contains-suspension-point.js。官方文档列出的三条已知局限规则文档明确声明了检测能力的边界理解这些局限有助于正确评估它的覆盖面嵌套函数一律标记即使嵌套函数内部完全同步例如array.map(() event.currentTarget)也会被报错。理由是无法静态证明其执行时机官方建议显式保存到变量以表达同步引用的意图。非流敏感分析只要源码中更早的位置出现过await或yieldevent.currentTarget访问就会被标记即使两者处于互斥分支、永远不可能同时执行例如condition ? await foo() : event.currentTarget。这是为了分析简单与确定性的权衡。无法追踪事件对象传递如果把整个event对象作为参数传给另一个函数规则无法得知该函数内部何时访问currentTarget因此这类迟到访问不会报错function handleEvent(event) { console.log(Too late for, event.currentTarget); // 规则不会报告 } element.addEventListener(click, event { setTimeout(handleEvent, 1, event); });测试用例揭示的边界行为规则测试覆盖了 30 个场景test/no-late-current-target-access.js其中几个边界值得关注同步赋值目标上的读取不报错event.onclick async event { event.currentTarget.href await getUrl(...); }—— 赋值目标在右值求值含await之前同步求值因此合法测试第 26 行但若此前已有await则同样报错测试第 186-191 行。作为已await调用的实参不报错await foo(event.currentTarget)中参数在挂起前同步求值而log(await ready(), event.currentTarget.value)中后一个实参在前一个await之后求值会报错测试第 104 行。for await...of的迭代器表达式不报错for await (const chunk of event.currentTarget.stream())中迭代器在首次挂起前求值合法测试第 32 行。生成器 handleryield同样会挂起事件派发function * (event) { yield ...; event.currentTarget... }会被报错测试第 93-98 行。解构参数不报错async ({currentTarget}) { await ...; currentTarget.disabled false; }中currentTarget是解构出的独立变量不是成员访问天然安全测试第 70-75 行。遮蔽参数外层 handler 已挂起、但内层回调声明了自己的event参数时内层访问按内层参数判定不报错测试第 52-59 行。嵌套异步回调内层回调自身await后再访问外层event.currentTarget会被归为in-nested-function而非after-suspension测试第 212-219 行。配置方式与规则注册该规则在 rules/index.js 中注册规则元数据声明recommended: true规则源码因此它默认包含在recommended配置中而在unopinionated配置中处于禁用状态见 docs/rules/no-late-current-target-access.md。想单独开启或关闭可在 ESLint 配置中{ rules: { unicorn/no-late-current-target-access: error, unicorn/no-late-current-target-access: off } }该规则不提供任何可配置选项仅针对js/js语言languages元数据中声明属于开箱即用的纯检测规则。小结一条规则背后的防御性编程理念no-late-current-target-access本质上是在用静态分析弥补浏览器事件生命周期这一运行时知识的盲区。它把await、yield、for await...of和延迟回调统一抽象为挂起点再结合事件参数命名模式与作用域解析精确打击同步阶段之后访问currentTarget这一整类隐性 bug。它有自己的保守边界非流敏感、不追踪对象传递但配合尽早把currentTarget存入变量的修复范式足以让事件处理器代码在异步化重构时保持安全。若你的代码同时涉及preventDefault()/stopPropagation()的时序问题可进一步查阅共享同一跟踪器的姊妹规则 no-late-event-control 及其源码 rules/no-late-event-control.js。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考