
ESLint no-plusplus 规则详解禁用一元自增自减运算符/--【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint导读no-plusplus是 ESLint 核心规则之一规则类型suggestion用于禁止在 JavaScript 代码中使用一元运算符和--。本规则存在的根本原因在于这两个运算符受**自动分号插入ASI**机制影响源码中空白字符的细微差异就可能彻底改变代码语义从而引入难以察觉的 bug。读完本文你将理解/--的 ASI 陷阱、该规则的判定逻辑与两种替代写法 1/- 1、可选配置项allowForLoopAfterthoughts的精确语义以及它在 ESLint 源码中的实现原理并能在扁平配置flat config中直接落地启用。为什么要禁用和--自动分号插入的语义陷阱一元运算符自增和--自减是 JavaScript 中语法最简洁的赋值方式但简洁的背后隐藏着自动分号插入Automatic Semicolon Insertion, ASI带来的歧义。ESLint 官方文档docs/src/rules/no-plusplus.md明确指出由于和--受自动分号插入影响代码中空白的不同会改变源码的语义。看下面两组完全合法、但语义截然不同的代码let i 10; let j 20; i j // i 11, j 20let i 10; let j 20; i j // i 10, j 21第一段代码中i 与j之间没有换行ASI 在i 之后插入分号因此i被自增为 11j保持 20第二段代码中i、、j被换行隔开ASI 在i之后插入分号于是语句被解析为i; j;结果是i保持 10、j被前置自增为 21。同样的三个 token只因换行位置不同行为完全不同——这正是/--在团队代码中容易埋雷的地方也是no-plusplus规则存在的核心理由。Rule Details规则如何判定违规该规则的核心语义是禁止一元运算符和--lib/rules/no-plusplus.js。从规则元数据看见 docs/src/_data/rules_meta.json元数据项值typesuggestionrecommendedfalse不在eslint:recommended中需手动开启fixablefalse不提供自动修复hasSuggestionsfalse不提供编辑器建议修复frozentrue核心冻结规则语义不允许随意变更规则通过监听 AST 中的UpdateExpression节点来工作。所有/--表达式无论前缀形式i还是后缀形式i在 AST 中都是UpdateExpression节点节点上带有operator字段或--。当访问到该节点时规则会报告错误错误消息为Unary operator {{operator}} used.其中{{operator}}会被替换为实际的运算符或--。错误的代码示例以下代码均会触发no-plusplus报错docs/src/rules/no-plusplus.md/*eslint no-plusplus: error*/ let foo 0; foo; let bar 42; bar--; for (let i 0; i l; i) { doSomething(i); }注意即使/--出现在for循环的更新子句中上例的i默认配置下同样会被判定为违规。正确的代码示例推荐使用复合赋值运算符与-作为替代语义等价且不受 ASI 影响/*eslint no-plusplus: error*/ let foo 0; foo 1; let bar 42; bar - 1; for (let i 0; i l; i 1) { doSomething(i); }一个值得注意的边界情况一元加号和一元减号-不受本规则约束。在 tests/lib/rules/no-plusplus.js 的valid用例中var foo 0; foo1;被判定为合法——因为foo 1是普通赋值表达式AST 节点类型为AssignmentExpression并非UpdateExpression。需要区分的是/--更新运算与/-一元正负号是完全不同的两种运算。配置选项allowForLoopAfterthoughtsno-plusplus接受一个对象选项docs/src/rules/no-plusplus.md选项类型默认值说明allowForLoopAfterthoughtsbooleanfalse允许在for循环的更新子句afterthought即第三个表达式中使用和--在 ESLint 的扁平配置flat config中启用该规则并开启此选项的写法如下// eslint.config.js export default [ { rules: { no-plusplus: [error, { allowForLoopAfterthoughts: true }] } } ];选项的默认值也可以在源码与数据文件中得到印证规则实现的defaultOptions为[{ allowForLoopAfterthoughts: false }]lib/rules/no-plusplus.js与 docs/src/_data/rules_meta.json 中记录的一致。开启选项后的正确示例/*eslint no-plusplus: [error, { allowForLoopAfterthoughts: true }]*/ for (let i 0; i l; i) { doSomething(i); } for (let i l; i 0; i--) { doSomething(i); } for (let i 0, j l; i l; i, j--) { doSomething(i, j); }这三种模式覆盖了for循环更新子句的典型形态单变量自增、单变量自减、以及通过逗号运算符SequenceExpression同时更新多个变量——它们都属于 for 循环的 afterthought被放行。开启选项后依然违规的示例该选项的放行范围仅限于for循环的更新子句其他任何位置出现的/--仍然报错docs/src/rules/no-plusplus.md/*eslint no-plusplus: [error, { allowForLoopAfterthoughts: true }]*/ for (let i 0; i l; j i) { doSomething(i, j); } for (let i l; i--;) { doSomething(i); } for (let i 0; i l;) i;逐条分析这三处报错原因for (let i 0; i l; j i)i出现在赋值表达式内部j i该赋值表达式才是更新子句i本身是赋值右侧的子表达式不满足更新子句本身的条件for (let i l; i--;)i--出现在测试子句第二个表达式中而非更新子句for (let i 0; i l;) i;i出现在循环体内部与更新子句无关。源码实现for 循环 afterthought 的精确定义理解allowForLoopAfterthoughts到底放行了什么需要看规则实现中的两个辅助函数lib/rules/no-plusplus.js。isForStatementUpdate直接命中更新子句function isForStatementUpdate(node) { const parent node.parent; return parent.type ForStatement parent.update node; }该函数判定节点的父节点是ForStatementfor语句且该节点就是父节点的update字段。只有同时满足这两个条件才算直接命中for循环的更新子句。isForLoopAfterthought穿透嵌套的逗号表达式function isForLoopAfterthought(node) { const parent node.parent; if (parent.type SequenceExpression) { return isForLoopAfterthought(parent); } return isForStatementUpdate(node); }该函数递归向上穿透任意层级的SequenceExpression逗号表达式只要当前节点的父节点是逗号表达式就继续向父级查找直到找到非逗号表达式的祖先再判断其是否为ForStatement的update。这正是前面正确示例中for (let i 0, j l; i l; i, j--)得以放行的原因——i的父节点是逗号表达式i, j--向上穿透后命中ForStatement的update。主逻辑create(context) { const [{ allowForLoopAfterthoughts }] context.options; return { UpdateExpression(node) { if (allowForLoopAfterthoughts isForLoopAfterthought(node)) { return; } context.report({ node, messageId: unexpectedUnaryOp, data: { operator: node.operator, }, }); }, }; }核心逻辑非常简洁在UpdateExpression访问器中若开启了allowForLoopAfterthoughts且当前节点是 for 循环的 afterthought则直接return跳过否则报告unexpectedUnaryOp错误。从源码结构可以推断规则对前缀形式i与后缀形式i一视同仁——它们都是UpdateExpression节点唯一的区别只是节点上的prefix布尔字段并不影响判定。规则在 lib/rules/index.js 中以懒加载方式注册no-plusplus: () require(./no-plusplus)与其余核心规则保持一致。测试用例佐证afterthought 的判定边界测试文件 用大量用例精确锁定了该规则的判定边界是理解规则语义的最佳参考放行valid开启选项后for (i 0; i l; i) { console.log(i); } for (var i 0, j i 1; j example.length; i, j) {} for (;; i--, foo()); for (;; foo(), --i); for (;; foo(), i, bar); for (;; i, (j, k--)); for (;; foo(), (bar(), i), baz()); for (;; (--i, j 2), bar j 1); for (;; a, (i--, (b, j, c)), d);注意最后几个用例(j, k--)、(bar(), i)、a, (i--, (b, j, c)), d都是多层嵌套逗号表达式无论嵌套多深只要最终祖先命中ForStatement的update就会被isForLoopAfterthought的递归逻辑正确识别。报错invalid开启选项后var foo 0; foo; // 普通表达式报错 for (i 0; i l; i) { v; } // 循环体内的 报错 for (i;;); // 初始化子句中的 报错 for (;--i;); // 测试子句中的 --报错 for (;;) i; // 循环体内的 报错 for (;; i j); // 赋值内部非序列表达式的 报错 for (;; i, f(--j)); // 嵌套在函数调用实参中的 --报错 for (;; foo (i, bar)); // 嵌套在二元表达式中的 报错其中for (;; i j)与for (;; foo (i, bar))特别值得注意i的父节点分别是AssignmentExpression和BinaryExpression都不是SequenceExpression因此isForLoopAfterthought不会向上穿透直接判定违规。只有当/--自身处于或嵌套于逗号表达式后处于更新子句中时才会被放行。测试中还验证了错误报告的messageId为unexpectedUnaryOp且data.operator会精确携带或--。在项目中启用 no-plusplus由于no-plusplus的recommended为false参见 docs/src/_data/rules.json 与 docs/src/_data/rules_meta.json它不会随eslint:recommended自动开启需要显式配置。在本仓库的扁平配置flat config体系中参见仓库根目录的 eslint.config.js可按需选择以下两种策略严格模式——全面禁止export default [ { rules: { no-plusplus: error } } ];宽松模式——仅在 for 循环更新子句放行export default [ { rules: { no-plusplus: [error, { allowForLoopAfterthoughts: true }] } } ];由于规则不提供自动修复fixable: false启用后需要开发者手动将i/i--改写为i 1/i - 1或用for循环的更新子句替代写法。建议团队结合自身编码风格决定追求绝对语义清晰与可读性时采用严格模式习惯在循环中使用i且能接受 ASI 边界情况时可开启allowForLoopAfterthoughts以获得二者兼顾的体验。小结no-plusplus禁止/--核心理由是自动分号插入会让空白差异改变语义代码可读性与确定性降低规则监听UpdateExpression节点前缀、后缀形式均被覆盖替代写法为 1/- 1可选配置allowForLoopAfterthoughts放行for循环更新子句中的/--源码通过isForLoopAfterthought递归穿透嵌套逗号表达式精确判定边界该规则属于suggestion类型、recommended: false、不可自动修复需在配置中显式启用。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考