十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

eslint no-param-reassign 规则详解:禁止修改函数参数,从配置到源码实现

eslint no-param-reassign 规则详解:禁止修改函数参数,从配置到源码实现 eslint no-param-reassign 规则详解禁止修改函数参数从配置到源码实现【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint本指南以 eslint 官方规则文档 no-param-reassign.md 为核心系统讲解该规则的设计动机、props/ignorePropertyModificationsFor/ignorePropertyModificationsForRegex三个配置项的完整用法并结合本仓库源码 lib/rules/no-param-reassign.js 与测试用例 tests/lib/rules/no-param-reassign.js 剖析其内部判定逻辑。读完本文你将掌握该规则的配置方法、边界行为与底层实现原理能够在自己的项目中准确启用并定制它。规则动机为什么不应重新赋值函数参数对函数参数的重新赋值容易造成误导并引发令人困惑的行为。在非strict模式下修改函数参数会同步变更arguments对象中的索引值导致函数内部与调用方对参数状态的认知出现偏差该行为在strict模式下的影响见下文 何时不使用此规则 小节。多数情况下对参数赋值并非开发者本意而是笔误或逻辑错误的信号。除重新赋值外该规则还可配置为在函数参数被修改包括其属性被修改时同样报告错误。参数上的副作用会产生反直觉的执行流程让错误变得难以定位。因此该规则被归类为suggestion类型可在 conf/rule-type-list.json 的类型清单中查看该类型定义用于在编码阶段提前暴露潜在的参数滥用。Rule Details规则检查什么该规则的目标是阻止因修改或重新赋值函数参数而引发的非预期行为。其默认行为不配置任何选项时覆盖以下四类直接写回参数的写法均为错误代码/*eslint no-param-reassign: error*/ const foo function(bar) { bar 13; // 直接赋值 } const foo1 function(bar) { bar; // 自增/自减UpdateExpression } const foo2 function(bar) { for (bar in baz) {} // for-in 循环左值 } const foo3 function(bar) { for (bar of baz) {} // for-of 循环左值 }而下面的代码是正确的因为它只是把参数读取后赋给一个新的局部变量没有改动参数本身/*eslint no-param-reassign: error*/ const foo function(bar) { const baz bar; }源码视角判定写回的核心逻辑在 lib/rules/no-param-reassign.js 中规则的create(context)通过sourceCode.getDeclaredVariables(node)获取函数声明变量再借助reference.isWrite()判断引用是否为写入引用从而报告assignmentToFunctionParam消息消息模板为Assignment to function parameter {{name}}.。值得注意的细节规则监听FunctionDeclaration:exit、FunctionExpression:exit、ArrowFunctionExpression:exit三种节点且必须使用:exit阶段因为报告时需要依赖node.parent属性完成回溯判定。checkReference中通过index 0 || references[index - 1].identifier ! identifier去重避免解构赋值中同一标识符产生多个可写引用时重复报告——解构赋值的默认值可能产生多个对同一标识符的写引用见 lib/rules/no-param-reassign.js。该规则基于Reference/Variable的作用域分析机制工作仅当variable.defs[0].type Parameter时才检查因此对全局变量的赋值如someGlobal 13不会误报这在测试用例中有明确覆盖。该规则于 0.18.0 版本加入 eslint见 docs/src/_data/rule_versions.json并默认不推荐recommended: false见 docs/src/_data/rules_meta.json。Options三个配置项详解该规则接受一个对象选项包含布尔属性props以及两个数组属性ignorePropertyModificationsFor与ignorePropertyModificationsForRegex。默认值为props默认falseignorePropertyModificationsFor默认空数组[]ignorePropertyModificationsForRegex默认空数组[]。当props为true时规则会额外警告对参数属性的修改除非该参数名出现在上述两个忽略数组中。从 lib/rules/no-param-reassign.js 的schema定义可以看出配置存在两套互斥结构oneOf当props为false时不允许携带其他属性additionalProperties: false当props为true时才允许同时配置两个忽略数组且数组元素为字符串、不允许重复uniqueItems: true。props: false默认行为在默认配置{ props: false }下修改参数对象的属性不会被报告以下代码全部正确/*eslint no-param-reassign: [error, { props: false }]*/ const foo function(bar) { bar.prop value; } const foo1 function(bar) { delete bar.aaa; } const foo2 function(bar) { bar.aaa; } const foo3 function(bar) { for (bar.aaa in baz) {} } const foo4 function(bar) { for (bar.aaa of baz) {} }这种模式非常适合只读参数、可改写其内容的场景例如向传入的配置对象或缓存对象写入字段。props: true同时检查属性修改当配置为{ props: true }时上述五类对参数属性的修改赋值、delete、自增、for-in/for-of左值全部变为错误/*eslint no-param-reassign: [error, { props: true }]*/ const foo function(bar) { bar.prop value; } const foo1 function(bar) { delete bar.aaa; } const foo2 function(bar) { bar.aaa; } const foo3 function(bar) { for (bar.aaa in baz) {} } const foo4 function(bar) { for (bar.aaa of baz) {} }源码视角isModifyingProp 如何识别属性修改在props: true时规则调用 lib/rules/no-param-reassign.js 中的isModifyingProp(reference)从参数标识符沿父节点向上回溯直到遇到停止节点为止。stopNodePattern匹配以Statement、Declaration、Function(?:Expression)、Program结尾的节点类型但对ForInStatement与ForOfStatement特殊放行以便继续深入判定循环左值。回溯过程中的关键分支全部有对应测试用例佐证包括AssignmentExpression仅当参数标识符是赋值左侧parent.left node时才判定为修改例如bar.a 0UpdateExpression如bar.a直接判定为修改UnaryExpression仅当操作符为delete如delete bar.a时判定为修改ForInStatement/ForOfStatement参数标识符位于parent.left时判定为修改如for (bar.a in baz)明确排除的写法bar.get(0).a 0CallExpression中参数不在 callee 位置、data[bar.a] 0MemberExpression中参数位于 property 位置、({ [bar]: a } value)Property的 key、(bar ? a : b).c barConditionalExpression的 test——这些场景中参数并未被直接修改不应误报。测试文件 tests/lib/rules/no-param-reassign.js 的valid数组中大量此类排除场景用例正是对这些边界行为的回归保障。ignorePropertyModificationsFor按参数名精确忽略当props: true且某些参数确实允许修改其属性时可以按精确名称列出例外。配置了ignorePropertyModificationsFor: [bar]后以下对bar属性的各类修改均为正确代码/*eslint no-param-reassign: [error, { props: true, ignorePropertyModificationsFor: [bar] }]*/ const foo function(bar) { bar.prop value; } const foo1 function(bar) { delete bar.aaa; } const foo2 function(bar) { bar.aaa; } const foo3 function(bar) { for (bar.aaa in baz) {} } const foo4 function(bar) { for (bar.aaa of baz) {} }该数组同样支持并列多个参数名例如测试中的ignorePropertyModificationsFor: [a, x]见 tests/lib/rules/no-param-reassign.js。在 lib/rules/no-param-reassign.js 的isIgnoredPropertyAssignment中该数组通过Array.prototype.includes做全等匹配。ignorePropertyModificationsForRegex按正则模式忽略当需要按命名模式忽略一批参数时可使用正则数组。配置ignorePropertyModificationsForRegex: [^bar]后所有以bar开头的参数名如barVar、barrito、bar_、barBaz对属性的修改均为正确代码/*eslint no-param-reassign: [error, { props: true, ignorePropertyModificationsForRegex: [^bar] }]*/ const foo function(barVar) { barVar.prop value; } const foo1 function(barrito) { delete barrito.aaa; } const foo2 function(bar_) { bar_.aaa; } const foo3 function(barBaz) { for (barBaz.aaa in baz) {} } const foo4 function(barBaz) { for (barBaz.aaa of baz) {} }在源码实现中该数组的每个字符串会在运行时通过new RegExp(ignored, u)构造为正则对象u标志表示 Unicode 模式见 lib/rules/no-param-reassign.js并对参数名执行.test()。因此这里的字符串是正则表达式而非普通通配符例如测试中使用的^a.*$、^(foo|bar)$等模式见 tests/lib/rules/no-param-reassign.js。注意正则匹配是大小写敏感的测试中^B.*$无法匹配bar仍会报错。两个忽略数组可以同时使用ignorePropertyModificationsForRegex负责正则匹配ignorePropertyModificationsFor负责精确匹配两者任一命中即放行。常见边界场景速查结合源码与测试下表汇总了常见写法在props: true下的判定结果便于快速查阅代码片段判定结果依据bar 13报错assignmentToFunctionParam直接写引用bar/bar/bar--/--bar报错UpdateExpression 写引用bar 13报错复合赋值for (bar in baz)/for (bar of baz)报错循环左值({bar} {})、[...bar] obj、({...bar} obj)报错解构赋值写回a b、a \|\| b、a ?? b报错逻辑赋值ES2021见测试用例bar.a 0、delete bar.a、bar.a、for (bar.a in {})报错assignmentToFunctionParamProp属性修改a.b c、a[b] ?? c报错逻辑赋值作用于属性data[bar.a] 0、bar.get(0).a 0、({ [bar]: a } value)不报错参数非修改主体源码显式排除对全局变量赋值someGlobal 13不报错仅检查 Parameter 定义变量此外function foo(a) { (function() { var a 12; a; })(); }不会被报告——内层函数中声明的是同名局部变量而非外层参数作用域分析能正确区分。配置示例在 eslint.config.js 中使用在 flat config 体系下可以这样在 eslint.config.js 中按需启用该规则。例如默认关闭属性检查仅禁止参数重新赋值// eslint.config.js export default [ { rules: { no-param-reassign: [error, { props: false }] } } ];若希望函数不能以任何方式改动传入的参数对象适合工具函数、纯函数为主的代码库// eslint.config.js export default [ { rules: { no-param-reassign: [error, { props: true }] } } ];对于允许在参数对象上写入但禁止替换参数引用的常见模式例如缓存对象、配置收集器推荐组合使用// eslint.config.js export default [ { rules: { no-param-reassign: [ error, { props: true, ignorePropertyModificationsFor: [cache, options], ignorePropertyModificationsForRegex: [^ctx] } ] } } ];该规则模块通过 lib/rules/index.js 的懒加载机制注册no-param-reassign: () require(./no-param-reassign)因此可以直接以no-param-reassign作为规则名使用无需额外安装插件。When Not To Use It何时应关闭此规则如果你希望允许对函数参数赋值可以放心地关闭该规则——它的目的只是帮助你规避非预期的参数修改并非语言层面的强制约束。需要特别说明的是与strict模式的关系strict模式下的代码不会将arguments对象的索引与各个参数绑定同步。因此在 ESM 模块或其他strict模式函数中并不存在改参数导致arguments对象被连带改动的问题也就无需靠该规则来防护arguments对象的意外变更。换句话说该规则对arguments的保护价值主要体现在非strict模式的代码中在纯strict模式项目里是否启用它更多取决于你的团队对参数是否可变这一编程约定的偏好。延伸阅读规则完整文档docs/src/rules/no-param-reassign.md规则源码实现lib/rules/no-param-reassign.js规则测试用例tests/lib/rules/no-param-reassign.js规则元数据类型、推荐状态、文档链接docs/src/_data/rules_meta.json规则引入版本记录docs/src/_data/rule_versions.json规则类型分类体系conf/rule-type-list.json【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表