ESLint no-caller 规则深度解析:禁用 arguments.caller 与 arguments.callee
ESLint no-caller 规则深度解析禁用 arguments.caller 与 arguments.callee【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslintno-caller是 ESLint 内置的suggestion类规则用于在 JavaScript 代码中拦截对已废弃的arguments.caller与arguments.callee属性的访问。本文以 ESLint 仓库中该规则的 官方文档 为骨架结合 规则源码 与其 单元测试完整讲解该规则的触发条件、边界行为、配置方式与底层实现帮助你理解为何要禁止这两个属性以及如何在项目中正确开启并替换这类写法。背景为什么arguments.caller与arguments.callee需要被禁用每个非箭头函数内部都隐式存在一个arguments对象用于保存实际传入的实参。历史上这个对象上还挂着两个特殊的属性arguments.callee指向当前正在执行的函数本身arguments.caller指向调用当前函数的那个函数如果存在。它们曾经被用来实现匿名函数递归、调用栈回溯等技巧但代价是让引擎无法对函数做尾调用优化、内联优化等多项代码优化因此它们已在新版 JavaScript 规范中被标记为废弃deprecated在 ECMAScript 5 的严格模式strict mode下访问arguments.caller和arguments.callee会被直接判定为语法错误并抛错。例如下面这段代码在严格模式下根本无法运行function foo() { const callee arguments.callee; // TypeError / SyntaxError }ESLint 的no-caller规则正是为了在编码阶段就阻止这类“既阻碍优化、又会在严格模式下直接崩溃”的废弃写法进入代码库。规则元信息在 lib/rules/no-caller.js 中定义如下type: suggestion属于建议类规则不会造成运行时错误但强烈建议修改recommended: false未包含在eslint:recommended预设中需要开发者显式开启schema: []规则不接收任何配置选项。Rule Details规则何时触发、何时放行根据官方文档的说明本规则的目标是“劝阻使用已废弃且次优的代码”做法是禁止使用arguments.caller和arguments.callee只要代码中出现对这两个属性的访问规则就会报告警告。结合 lib/rules/no-caller.js 的实现触发条件可以精确拆解为出现了一个MemberExpression成员表达式成员表达式的对象名node.object.name严格等于arguments该成员访问不是计算属性形式即代码写的是arguments.callee而非arguments[callee]源码中通过!node.computed判断属性名node.property.name匹配正则/^calle[er]$/u即恰好是caller或callee。一旦命中规则会报告一条消息消息模板为Avoid arguments.{{prop}}.其中{{prop}}会被替换成实际访问的属性名因此报告文案是Avoid arguments.caller.Avoid arguments.callee.什么是不应该被误报的合法用法arguments对象上还有其他完全合法的属性本规则不会误伤arguments.length实参个数arguments[0]、arguments[n]按索引访问实参整个arguments对象本身的传递。这一点在 tests/lib/rules/no-caller.js 的valid用例中得到了直接印证var x arguments.length; // 合法 var x arguments; // 合法 var x arguments[0]; // 合法 var x arguments[caller] // 合法caller 是变量名且属于计算属性访问注意最后一个用例arguments[caller]中的caller只是一个普通变量访问的是arguments对象上名为该变量值的动态属性与arguments.caller语义完全不同因此不触发规则。不正确的代码示例官方文档给出了两类典型的不正确写法一种是直接使用arguments.callee实现匿名函数递归另一种是在数组遍历回调中借助arguments.callee计算阶乘。它们都应被规则拦截/*eslint no-caller: error*/ function foo(n) { if (n 0) { return; } arguments.callee(n - 1); // 报错Avoid arguments.callee. } [1,2,3,4,5].map(function(n) { return !(n 1) ? 1 : arguments.callee(n - 1) * n; // 报错Avoid arguments.callee. });正确的代码示例现代替代写法修复方式非常直接给函数一个名字然后直接用函数名递归彻底摆脱对arguments.callee的依赖/*eslint no-caller: error*/ function foo(n) { if (n 0) { return; } foo(n - 1); // 直接使用函数名递归 } [1,2,3,4,5].map(function factorial(n) { return !(n 1) ? 1 : factorial(n - 1) * n; // 命名函数表达式 });第一处用具名函数foo自递归第二处把匿名回调改为命名函数表达式named function expression在函数体内通过名字factorial引用自身。两种写法都移除了arguments.callee且完全兼容严格模式也便于引擎优化。Options本规则没有任何配置项官方文档明确说明This rule has no options.此规则没有选项。这一点在源码中同样有据可查schema: []见 lib/rules/no-caller.js表示规则既不接受选项对象也不支持数组形式的配置参数。因此配置时只能使用off/warn/error三档严重级别任何额外传入的选项都会被 ESLint 判定为配置错误。源码实现解析规则如何在 AST 上工作规则实现位于 lib/rules/no-caller.js全文结构非常精简一个meta块定义元信息一个create(context)返回 AST 访问器。create(context) { return { MemberExpression(node) { const objectName node.object.name, propertyName node.property.name; if ( objectName arguments !node.computed propertyName propertyName.match(/^calle[er]$/u) ) { context.report({ node, messageId: unexpected, data: { prop: propertyName }, }); } }, }; }值得注意的实现细节只监听MemberExpression节点规则并不关心arguments.callee出现在什么语句里赋值、调用、返回值均可只要 AST 中出现arguments.caller/arguments.callee形式的成员访问就会被捕获正则/^calle[er]$/u的精妙之处calle[er]恰好同时覆盖caller和callee两种拼写callee以e结尾caller以r结尾不检查计算属性arguments[callee]这种写法属于动态访问规则的检查范围刻意限定在静态的点号访问形式报告使用messageId机制消息文案统一维护在meta.messages中unexpected: Avoid arguments.{{prop}}.通过data.prop动态填充属性名方便 i18n 与消息复用。此外规则通过 lib/rules/index.js 中的no-caller: () require(./no-caller)懒加载注册与其他 300 余条内置规则一样随 ESLint 分发无需安装任何插件。测试用例验证规则行为的完整规格单元测试 使用RuleTester对规则行为做了精确的规格化描述可作为规则行为的“可执行文档”合法用例validarguments.length、裸arguments、arguments[0]、arguments[caller]变量形式均不报错非法用例invalidarguments.callee与arguments.caller各触发一条unexpected消息且data.prop分别为callee、caller。invalid: [ { code: var x arguments.callee, errors: [{ messageId: unexpected, data: { prop: callee } }] }, { code: var x arguments.caller, errors: [{ messageId: unexpected, data: { prop: caller } }] }, ]在仓库中运行对应的规则测试即可验证npx mocha tests/lib/rules/no-caller.js如何在项目中启用 no-caller由于recommended: false该规则不会随eslint:recommended自动生效需要显式配置。在基于 flat config 的eslint.config.js中// eslint.config.js export default [ { rules: { no-caller: error, }, }, ];在传统.eslintrc风格配置中{ rules: { no-caller: error } }建议与no-eval、no-implied-eval等规则配合使用共同约束代码中对“动态执行与运行时反射”类特性的滥用。使用边界与注意事项严格模式下是硬性错误在use strict代码中访问arguments.callee会直接抛出TypeErrorno-caller能帮助你在运行前发现这类隐患Function.prototype.caller/callee不在检查范围内本规则只匹配arguments对象上的这两个属性。通过函数对象的fn.caller等反射访问属于另一类问题需要其他手段治理arguments.callee.caller等链式访问同样会命中只要成员表达式的对象是arguments、属性名匹配无论外层还嵌套了多少层.caller都会被报告不要用计算属性绕过规则虽然arguments[callee]当前不会被本规则拦截但它在严格模式下同样是禁用行为应从代码规范层面一并杜绝。总结no-caller是一条小而美的规则文档仅短短数段源码不足 60 行测试用例清晰覆盖了边界行为。它通过 AST 层面的精确匹配把两个“历史遗留、阻碍优化、严格模式即崩溃”的废弃属性挡在代码库之外。理解了它的触发条件arguments 非计算属性 caller/callee与替代方案具名函数自递归你就能在代码评审与日常开发中快速识别并修正这类写法。相关资源规则文档docs/src/rules/no-caller.md规则实现lib/rules/no-caller.js单元测试tests/lib/rules/no-caller.js规则注册lib/rules/index.js【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考