ESLint `logical-assignment-operators` 规则全解析:用 `||=`、`=`、`??=` 简化赋值代码
2026/9/11 16:26:42 网站建设 项目流程

ESLintlogical-assignment-operators规则全解析:用||=&&=??=简化赋值代码

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

本文是 ESLint 内置规则logical-assignment-operators的完整技术指南。该规则针对 ES2021 引入的逻辑赋值运算符(||=&&=??=),既可以强制要求使用这些简写形式,也可以禁止使用以保持代码风格统一。读完本文,你将掌握该规则的两种字符串选项与enforceForIfStatements对象选项的完整配置方式,理解它如何识别"可缩短为逻辑赋值"的表达式(包括if语句中的等价模式),并能结合仓库源码看懂其底层的引用一致性判断、求值顺序保护与 getter 安全检查机制。

规则背景:ES2021 逻辑赋值运算符

ES2021 为逻辑运算符||&&??引入了赋值简写形式。在此之前,赋值简写仅适用于+*等数学运算(可参考同仓库的 operator-assignment 规则文档,它列出了一张完整的+=-=*=等简写对照表)。

逻辑赋值简写的前提是:赋值目标与逻辑表达式的左侧操作数引用相同。例如:

a = a || b // 可以简写为 a ||= b

三个逻辑赋值运算符的行为各不相同,这一点与数学运算简写有本质区别:

简写形式等价写法语义
a \|\|= ba = a \|\| b仅当a为 falsy 时才赋值
a &&= ba = a && b仅当a为 truthy 时才赋值
a ??= ba = a ?? b仅当anull/undefined时才赋值

由于这三个运算符都具备短路求值特性,它们与operator-assignment规则所覆盖的数学运算简写行为不同,因此在 operator-assignment 规则文档 中被明确排除在外,交由本规则独立处理。

规则详情与默认行为

该规则由 lib/rules/logical-assignment-operators.js 实现,规则元信息(meta)中声明了type: "suggestion"(建议类规则)、fixable: "code"(可自动修复)以及hasSuggestions: true(同时提供手动建议)。recommended: false表明它不在eslint:recommended预设中,需要开发者显式启用。

规则要求或禁止使用逻辑赋值运算符简写,可通过以下配置控制:

字符串选项

选项默认值行为
"always"✅ 默认要求尽量使用逻辑赋值简写
"never"禁止使用逻辑赋值简写

对象选项(仅当字符串选项为"always"时可用)

配置项默认值行为
enforceForIfStatements: false✅ 默认不检查等价的if语句
enforceForIfStatements: true检查可改写为逻辑赋值的if语句

从源码的 schema 定义(lib/rules/logical-assignment-operators.js)可以看出选项的组合约束:"always"模式可携带最多 1 个{ enforceForIfStatements: boolean }对象(minItems: 0允许不传任何选项,即纯默认配置),而"never"模式不允许携带对象选项(maxItems: 1)。默认选项defaultOptions: ["always"]意味着即使不写配置,规则也按"always"生效。

选项"always":强制使用简写

"always"模式下,规则会检查所有可以被逻辑赋值运算符缩短的表达式。例如a = a || b可以缩短为a ||= b

针对多个操作数连续拼接的表达式,规则有专门的关联性(associativity)处理策略:形如a = a || b || c的表达式默认被报告为可缩短为a ||= b || c;但如果开发者使用括号显式定义了求值顺序,例如a = (a || b) || c,则不会被报告——此时括号的存在意味着作者有意控制短路边界。

这一行为在源码中由getLeftmostOperand函数实现(lib/rules/logical-assignment-operators.js):它沿着逻辑表达式的左侧不断下钻,一旦发现某个左侧子表达式被括号包裹(isParenthesised为真),就停止下钻并保留该位置,从而尊重括号表达的求值意图。

"always"下的错误(incorrect)代码示例

/*eslint logical-assignment-operators: ["error", "always"]*/ a = a || b a = a && b a = a ?? b a || (a = b) a && (a = b) a ?? (a = b) a = a || b || c a = a && b && c a = a ?? b ?? c

除了a = a || b这类赋值形式,规则还覆盖a || (a = b)这种逻辑表达式内嵌赋值的写法。从源码的监听器可见(lib/rules/logical-assignment-operators.js),always模式主要匹配两类节点:

  • AssignmentExpression[operator='='][right.type='LogicalExpression']:形如foo = foo || bar,先通过isSameReference校验左右引用一致,再检查最左操作数;
  • LogicalExpression[right.type="AssignmentExpression"][right.operator="="]:形如foo || (foo = bar),此时右侧赋值必须加括号,否则会被解析为(foo || foo) = bar这种非法语法。

"always"下的正确(correct)代码示例

/*eslint logical-assignment-operators: ["error", "always"]*/ a = b a += b a ||= b a = b || c a || (b = c) if (a) a = b a = (a || b) || c

注意:if (a) a = b在默认配置下是正确代码,因为默认enforceForIfStatements: false不检查if语句;a = (a || b) || c因括号明确了求值顺序而豁免。测试文件 tests/lib/rules/logical-assignment-operators.js 的 valid 用例还补充了更多不报告的情形,例如a = a && b || c(混合运算符、左操作数不一致)、a = (a || b) || c加括号,以及a || (a ||= b)(右操作数本身已是逻辑赋值)等。

选项"never":禁止简写

"never"模式与"always"完全相反,用于要求团队统一采用显式的a = a || b写法,禁止使用||=&&=??=

从源码看(lib/rules/logical-assignment-operators.js),该模式监听所有AssignmentExpression,通过astUtils.isLogicalAssignmentOperator(定义于 lib/rules/utils/ast-utils.js)判断运算符是否为三个逻辑赋值之一,命中即报告unexpected("Unexpected logical operator assignment ({{operator}}) shorthand.")。

"never"下的错误(incorrect)代码示例

/*eslint logical-assignment-operators: ["error", "never"]*/ a ||= b a &&= b a ??= b

"never"下的正确(correct)代码示例

/*eslint logical-assignment-operators: ["error", "never"]*/ a = a || b a = a && b a = a ?? b

选项enforceForIfStatements:检查等价的if语句

当配置为["always", { enforceForIfStatements: true }]时,规则会额外识别一组可用逻辑赋值运算符表达if语句模式。源码中该检查由IfStatement[alternate=null]监听器完成(lib/rules/logical-assignment-operators.js),它要求:if语句没有else分支、函数体要么是空块要么只含一条语句(ifNode.consequent.body.length === 1)、函数体是赋值表达式语句。

关键在于getExistence函数(lib/rules/logical-assignment-operators.js),它将if的条件表达式归类为三类"存在性检查",并映射到对应的赋值运算符:

if条件形式识别的检查对应运算符
aBoolean(a)!!a(truthy)真值检查&&=
!a!Boolean(a)(falsy)假值检查\|\|=
a == nulla == void 0(隐式空值比较)空值检查??=
a === null \|\| a === undefined(显式空值比较)空值检查??=

条件识别的背后是一组精巧的辅助判断函数:isImplicitNullishComparison(源码)识别value == null/value == void 0形式的隐式空值比较,且要求其中一侧是引用、另一侧是null字面量或void 0表达式;isExplicitNullishComparison(源码)则识别value === null || value === undefined形式的双重严格比较——它要求两个比较操作数引用一致,且nullundefined各居一侧(顺序无所谓)。isUndefined(源码)通过isReferenceToGlobalVariable确保undefined是全局变量而非被局部遮蔽的标识符——这正是测试中用const undefined = 0场景验证的目的。

["always", { enforceForIfStatements: true }]下的错误(incorrect)代码示例

/*eslint logical-assignment-operators: ["error", "always", { enforceForIfStatements: true }]*/ if (a) a = b // <=> a &&= b if (!a) a = b // <=> a ||= b if (a == null) a = b // <=> a ??= b if (a === null || a === undefined) a = b // <=> a ??= b

["always", { enforceForIfStatements: true }]下的正确(correct)代码示例

/*eslint logical-assignment-operators: ["error", "always", { enforceForIfStatements: true }]*/ if (a) b = c if (a === 0) a = b

第一例中赋值目标b与条件引用a不一致,无法简写;第二例中a === 0不属于真值/假值/空值三类可识别检查(测试中if (a === b) a = bif (a === undefined) a = bif (a != null) a = b等也均不被报告),因此保持原样。

修复机制与 getter 安全保护

该规则在alwaysnever两个方向上都提供了自动修复(fix)与建议(suggestion)双重能力,具体走哪条路径由createConditionalFixer(源码)根据安全条件决定:安全时直接产出fix,否则降级为suggest数组中的单条建议。

安全判断的核心是cannotBeGetter(源码)与accessesSingleProperty(源码):

  • 对于标识符(Identifier):严格模式下或不在with块内时,认为其不可能触发 getter,可以安全自动修复;
  • 对于成员表达式(MemberExpression):仅当对象是IdentifierSuperThisExpression这些基础类型,且访问的是单一属性(计算属性a[b]要求b本身不是成员表达式或链式表达式)时才可自动修复,否则只提供建议。

例如测试用例a.b = a.b ?? coutputnull(不自动修复),但suggestions中给出了a.b ??= c的建议输出——因为属性a.b可能触发 getter,ESLint 保守地不直接修改代码。同理,with (object) a = a || b也仅产生建议。

此外,所有 fixer 在动手前都会检查sourceCode.getCommentsInside(...)若表达式内部存在注释则放弃自动修复(见测试中a /* between */ = a || ba = a || /* between */ boutput: null用例),避免在注释处破坏代码结构。

修复时的另一个细节是括号保护:在never模式把a ??= b展开为a = a ?? b时,若右操作数本身是逻辑表达式(??||/&&不可混用),fixer 会为右操作数补充括号,防止语义改变。

在配置文件中启用规则

该规则不在eslint:recommended预设中,需要显式配置。以下是在 ESLint 配置文件中的几种用法:

// eslint.config.js(flat config) export default [ { rules: { // 默认行为:强制使用简写(等价于 ["error", "always"]) "logical-assignment-operators": "error", // 显式指定 always + 检查 if 语句 "logical-assignment-operators": ["error", "always", { enforceForIfStatements: true }], // 禁止使用逻辑赋值简写 "logical-assignment-operators": ["error", "never"], }, }, ];

规则对应的语言版本要求:逻辑赋值运算符属于 ES2021 语法,运行环境(Node.js 或转译器)需支持 ES2021。测试文件 tests/lib/rules/logical-assignment-operators.js 中的RuleTester正是以ecmaVersion: 2021运行验证的;个别涉及私有字段的用例(如this.#prop || (this.prop = value))则使用ecmaVersion: 2022单独标注。

When Not To Use It:何时关闭此规则

使用逻辑赋值运算符简写本质上是一种风格选择。如果团队希望在具体场景中自行判断哪种写法可读性更好(例如在强调"尽量减少赋值副作用"的代码中偏好显式写法),完全可以直接关闭此规则,让开发者逐例决定。

如果选择启用,建议结合代码审查或编辑器自动修复(--fix)落地,以消除a = a || ba ||= b混用的不一致状态。由于规则自带安全机制——无法确认 getter 副作用时只给建议、不强行修改——即便在大型存量代码库中开启也相对稳妥。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询