ARTICLE DETAIL

资讯详情

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

ESLint consistent-this 规则深度解析:强制 `this` 别名的统一命名与赋值

ESLint consistent-this 规则深度解析:强制 `this` 别名的统一命名与赋值 ESLint consistent-this 规则深度解析强制this别名的统一命名与赋值【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslintconsistent-this是 ESLint 内置的 suggestion 类规则用于在必须捕获当前执行上下文即把this保存到普通变量的代码中强制团队约定的别名如that、self、me被一致地使用。读完本文你将掌握该规则的完整配置方式、两类核心校验逻辑别名必须赋值为this与this必须赋给约定别名并能通过仓库源码与测试用例理解它在作用域分析层面如何工作、有哪些边界情形如解构赋值、复合赋值、跨作用域赋值需要留意。为什么需要给this起别名JavaScript 的函数中this的取值取决于调用方式而不是定义位置。在回调函数中this往往不再指向外层上下文因此开发者常把外层this先保存到普通变量供回调内部使用。最常见的场景是 jQuery 回调const that this; jQuery(li).click(function (event) { // 这里 this 是触发 click 事件的 HTMLElement that.setFoo(42); });类似that、self、me这样被社区广泛使用的this别名还有很多。问题在于如果团队中有人用that、有人用self、有人用_this代码库会变得混乱。consistent-this规则的价值就在于把团队约定固化下来——无论约定哪个别名都必须在整个应用中保持一致。规则详解Rule Details依据 consistent-this 规则文档该规则对使用约定别名声明的变量施加两条强制约束如果声明了一个约定名称的变量它要么在声明处被初始化为this要么在声明所在的同一作用域内被赋值为this如果某个变量被初始化为或赋值为this那么这个变量的名字必须是约定别名之一。换句话说该规则从别名侧和this侧两个方向同时校验别名的赋值必须是this而this的接收者必须是别名。配置选项Options规则接受一个或多个字符串参数即约定的this别名列表默认值为that。例如在 ESLint 配置文件flat config 或 legacy 配置中启用{ rules: { consistent-this: [error, that] } }也可以同时约定多个别名例如[error, self, vm]。从源码看选项在 lib/rules/consistent-this.js 中通过 JSON Schema 校验每个选项必须是长度至少为 1 的字符串且整个数组不允许重复项uniqueItems: true。默认选项that下的错误示例/*eslint consistent-this: [error, that]*/ let that 42; // that 是约定别名却赋了 42 let self this; // 把 this 赋给了非约定别名 self that 42; // 对别名 that 的赋值不是 this self this; // 把 this 赋给了非约定别名 self默认选项that下的正确示例/*eslint consistent-this: [error, that]*/ let that this; // 别名初始化时直接指向 this const self 42; // 非别名变量赋任意值都不受约束 let foo; // 未初始化、后续也未赋 this无问题 that this; // 别名被赋值为 this foo.bar this; // 属性成员赋值不受规则约束注意最后一个示例this被赋给对象的属性foo.bar this而不是变量不属于规则管辖范围。这从实现上也可以得到印证——源码的AssignmentExpression处理器只检查node.left.type Identifier的左值见 lib/rules/consistent-this.js。变量未初始化时的处理如果约定别名that在声明时没有初始化则要求它在同一作用域内被赋值this。下面的代码是错误的/*eslint consistent-this: [error, that]*/ let that; // 声明时未初始化 function f() { that this; // 赋值发生在函数作用域内不算数 }而下面两种写法是正确的/*eslint consistent-this: [error, that]*/ let that; that this; // 在同一作用域内完成了 this 的赋值/*eslint consistent-this: [error, that]*/ let foo 42, that; // 多变量声明foo 正常初始化 that this; // that 在同一作用域内被赋值为 this源码实现规则如何在底层工作consistent-this的实现位于 lib/rules/consistent-this.js作者为 Raphael Pigulla规则类型为suggestion非problem、非layout默认不包含在推荐配置中recommended: false且被标记为frozen: true意味着该规则的选项行为与错误消息已冻结后续版本不会轻易变更见 lib/rules/consistent-this.js。它通过 lib/rules/index.js 中的懒加载映射注册consistent-this: () require(./consistent-this),两条错误消息规则定义了两条消息模板lib/rules/consistent-this.jsaliasNotAssignedToThisDesignated alias {{name}} is not assigned to this.—— 约定别名没有被赋值thisunexpectedAliasUnexpected alias {{name}} for this.—— 用非约定名字接收了this。核心判定checkAssignment无论是变量声明还是赋值表达式最终都会进入checkAssignment(node, name, value)lib/rules/consistent-this.js进行判定判定依据是被赋值表达式是否为ThisExpression节点function checkAssignment(node, name, value) { const isThis value.type ThisExpression; if (aliases.includes(name)) { if (!isThis || (node.operator node.operator ! )) { reportBadAssignment(node, name); } } else if (isThis) { context.report({ node, messageId: unexpectedAlias, data: { name }, }); } }其中值得注意的细节是node.operator node.operator ! 这一条件复合赋值运算符如、-也会触发错误因为self this本质上是在对别名做非this的算术操作。这一点在测试中有明确覆盖见 tests/lib/rules/consistent-this.js{ code: self this, options: [self], errors: [ { messageId: aliasNotAssignedToThis, data: { name: self } }, ], }未初始化别名的追踪checkWasAssigned对于声明未初始化、随后在同一作用域赋值的合法模式规则不能只检查声明节点还需要借助作用域分析判断变量是否在声明所在作用域内被赋过this。这部分由checkWasAssigned(alias, scope)完成lib/rules/consistent-this.js通过scope.set.get(alias)从当前作用域的变量集合中取出对应变量如果变量在某个VariableDeclarator中带有初始化器def.node.init ! null说明声明时已初始化直接通过否则遍历variable.references查找是否存在一个满足全部条件的引用reference.from scope赋值发生在同一作用域而不是内层函数writeExpr存在且类型为ThisExpressionwrite.parent.operator 必须是普通赋值而非复合赋值若找不到这样的引用则对每个声明节点报告aliasNotAssignedToThis。正是第 1 条条件保证了前面错误示例中function f() { that this; }会被判错——赋值虽然指向外层变量但发生在内层函数作用域reference.from不等于声明所在作用域。测试用例var self; (function() { self this; }())也验证了这一点见 tests/lib/rules/consistent-this.js。作用域检查的触发时机与模块作用域ensureWasAssigned在Program:exit、FunctionExpression:exit、FunctionDeclaration:exit三个时机执行lib/rules/consistent-this.js覆盖了顶层与函数体内两层场景。它还有一个微妙处理当源码是 ES Modulenode.sourceType module时程序顶层其实是模块作用域规则会额外检查scope.childScopes[0]确保模块顶层未初始化的别名同样被追踪lib/rules/consistent-this.js。对应测试覆盖了sourceType: module的情形见 tests/lib/rules/consistent-this.js。解构赋值被显式豁免一个值得注意的边界情形是解构赋值。由于解构const { a, b } this或const [x, y] this中变量名与this的对应关系并不直观规则选择不检查解构模式VariableDeclarator处理器通过id.type ArrayPattern || id.type ObjectPattern判断出解构后直接跳过lib/rules/consistent-this.js。测试中的destructuringTest辅助函数验证了四种解构写法均合法function destructuringTest(code) { return { code, options: [self], languageOptions: { ecmaVersion: 6 } }; } destructuringTest(var {foo, bar} this), destructuringTest(({foo, bar} this)), destructuringTest(var [foo, bar] this), destructuringTest([foo, bar] this),测试证据规则行为的完整边界tests/lib/rules/consistent-this.js使用 ESLint 官方的RuleTesterrequire(../../../lib/rule-tester/rule-tester)以ecmaVersion: 5、sourceType: script为默认环境运行全部用例可作为理解规则语义的权威参照。除上文已提到的复合赋值、跨作用域赋值、解构豁免外测试还确认了以下行为非别名变量无论赋什么值都不报错var self 42选项[that]、self 42选项[that]未初始化且从未赋this的非别名变量合法var self选项[that]多别名配置同时生效var self this; var vm this;选项[self, vm]反向情形同样报错var context this会触发unexpectedAliasvar that this选项[self]也会触发unexpectedAlias说明即使赋值内容正确只要名字不是约定别名依然违规。实际接入与排查建议要在项目中启用该规则按团队约定选择一个别名并在配置中声明即可。ESLint flat config 示例// eslint.config.js export default [ { rules: { consistent-this: [error, that], }, }, ];该规则属于 suggestion 类型默认不在eslint:recommended中需要显式开启。启用后若代码中出现that 42、var context this或未初始化别名未在声明作用域内赋this等情形ESLint 会给出对应错误消息修复方向要么把赋值改成this、要么改用约定别名、要么在声明处直接初始化。何时不使用该规则When Not To Use It如果代码中需要捕获嵌套的上下文即在多层回调里多次保存不同层级的thisconsistent-this会难以适用——这种代码本身通常难以阅读和维护官方建议优先考虑重构例如改用箭头函数它不绑定自己的this来消除对别名的依赖而不是单纯依赖该规则。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表