拓冰建站拓冰建站
首页 / 资讯中心 / 正文

ESLint `no-undefined` 规则完全指南:禁止使用 `undefined` 标识符,杜绝遮蔽与误读

ESLintno-undefined规则完全指南禁止使用undefined标识符杜绝遮蔽与误读【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslintundefined在 JavaScript 中并非关键字而是全局对象的一个属性——它既可能被历史版本覆盖ECMAScript 3也可能在任意作用域内被局部变量遮蔽ECMAScript 5 之后依然如此。no-undefined是 ESLint 核心规则集中的一条suggestion级规则它从标识符解析层面拦截代码中对undefined的所有引用与定义强制开发者改用void 0、typeof判断或不初始化变量等更稳健的写法。读完本文你将完整掌握该规则的设计动机、作用范围、全部错误/正确示例、基于作用域链的源码实现原理以及它与no-undef-init、no-void、no-shadow-restricted-names、no-global-assign四兄弟的协同用法。undefined的危险身份全局属性而非关键字要理解no-undefined为何存在首先必须厘清undefined在语言层面的特殊地位。正如规则文档 docs/src/rules/no-undefined.md 开头所强调的Theundefinedvariable in JavaScript is actually a property of the global object.也就是说undefined不是null那样的语言关键字。null永远指向同一个确定的值而undefined只是全局对象上的一个普通属性在ECMAScript 3时代开发者甚至可以显式地覆盖全局undefined的值在ECMAScript 5及之后虽然对全局undefined的赋值不再生效但局部作用域内的遮蔽shadowing依然完全合法。所谓遮蔽就是在一个函数或块级作用域内用const、let、var甚至函数参数、catch子句参数重新声明一个名为undefined的变量从而让该作用域内所有对undefined的读取都指向这个假值。规则文档给出了经典的危险示例function doSomething(data) { const undefined hi; // doesnt do what you think it does if (data undefined) { // ... } }这里data undefined实际上是在和字符串hi比较而不是判断data是否为未定义值——这正是一条极易产生隐蔽 bug 的路径。规则的风格主张彻底避开undefined既然undefined可能被覆盖或被遮蔽读取它就可能拿到意想不到的值一些风格指南因此主张在代码中完全不使用undefined这正是no-undefined规则所强制执行的约定。这些风格指南通常同时推荐三条配套做法见规则文档希望变量为undefined时直接不初始化。JavaScript 中所有未初始化的变量会自动获得undefined值例如let foo;与let foo undefined;效果等价但前者更简洁且规避了标识符本身判断一个值是否为undefined时使用typeof运算符例如typeof foo undefined确有必要生成undefined值时使用void运算符例如void 0。需要注意的是typeof foo undefined与foo undefined有一个关键差异前者对未声明的变量也安全不会抛出ReferenceError而后者在foo未声明时会直接报错这也是typeof方案更稳健的原因之一。Rule Details规则到底拦截什么no-undefined的目标是消除代码中对undefined的一切使用只要undefined被用作标识符规则就会报告一条Unexpected use of undefined.messageId 为unexpectedUndefined警告。从规则元数据lib/rules/no-undefined.js可以看到type: suggestion——属于风格建议类规则不含自动修复fixable未声明也不在recommended配置中schema: []——没有任何可配置选项recommended: false、frozen: true——规则行为已被冻结且不会因被配置为 recommended 而默认开启。不正确的代码示例以下代码都会触发no-undefined报告来自规则文档的incorrect示例/*eslint no-undefined: error*/ const foo undefined; // 用 undefined 初始化变量 const undefined foo; // 声明名为 undefined 的局部变量遮蔽 if (foo undefined) { // 与 undefined 直接比较 // ... } function baz(undefined) { // 用 undefined 作为形参名 // ... } bar(undefined, lorem); // 把 undefined 作为实参传递正确的代码示例以下写法完全合法来自规则文档的correct示例/*eslint no-undefined: error*/ const foo void 0; // 用 void 0 生成 undefined 值 const Undefined foo; // 大小写不同不是同一个标识符 if (typeof foo undefined) { // 用 typeof 判断未定义 // ... } global.undefined foo; // 作为属性名访问而非标识符 bar(void 0, lorem); // 用 void 0 作为实参注意最后一行global.undefined是属性访问而非标识符引用undefined在这里只是对象属性名因此不违反规则。这与测试文件中a.undefined、this.undefined、global[undefined]均判为 valid 的结论一致见 tests/lib/rules/no-undefined.js 的 valid 用例。源码透视基于作用域链的检测实现规则文档描述了做什么而 lib/rules/no-undefined.js 揭示了怎么做。该规则的核心实现建立在 ESLint 的**作用域分析scope analysis**能力之上而非简单的文本或语法节点匹配在Program:exit事件中通过sourceCode.getScope(node)拿到全局作用域用一个栈对全局作用域及其全部子作用域进行深度优先遍历const stack [globalScope]; while (stack.length) { const scope stack.pop(); stack.push(...scope.childScopes); checkScope(scope); }对每个作用域调用scope.set.get(undefined)取出名为undefined的变量绑定若该作用域内根本没有undefined这个变量既无全局引用也无局部声明直接跳过若有则遍历undefinedVar.references对所有非初始化引用!ref.init报告其标识符节点再遍历undefinedVar.defs对所有定义节点如var undefined;、function undefined() {}、形参声明等也逐一报告。function checkScope(scope) { const undefinedVar scope.set.get(undefined); if (!undefinedVar) { return; } const references undefinedVar.references; const defs undefinedVar.defs; // Report non-initializing references (those are covered in defs below) references .filter(ref !ref.init) .forEach(ref report(ref.identifier)); defs.forEach(def report(def.name)); }这里的ref.init过滤十分关键一个变量声明语句var undefined true;中undefined既是引用reference又是定义definition实现刻意将初始化引用与定义分开处理避免同一处代码被重复报告。测试用例对此有精确验证tests/lib/rules/no-undefined.js{ code: var undefined true; undefined false;, errors: [ { messageId: unexpectedUndefined, column: 5 }, // 定义处 { messageId: unexpectedUndefined, column: 23 }, // 后续引用处 ], }该用例断言两处分别位于第 5 列和第 23 列证明定义与引用各报告一次、互不重复。该规则在 lib/rules/index.js 中以惰性加载方式注册no-undefined: () require(./no-undefined)与其他核心规则保持一致。边界行为哪些看似是 undefined的写法不受影响测试套件为我们勾勒出规则的精确边界tests/lib/rules/no-undefined.js。**有效不报告**的情况包括void 0、void!0、void-0、void0、null拼写相近的普通标识符undefine、ndefined作为属性名出现a.undefined、this.undefined、global[undefined]作为对象字面量键名/方法名({ undefined: bar })、({ undefined() {} })、class Foo { undefined() {} }ES Module 中重命名导入/导出import { undefined as a } from foo、export { undefined } from foo等。**无效会报告**的情况包括覆盖各种定义与引用形态裸引用undefined、undefined.a、a[undefined]、undefined[0]、f(undefined)各类声明var undefined;、var undefined true;、function undefined() {}、function f(undefined) {}、try {} catch(undefined) {}解构与展开({ undefined })、var { undefined } foo、var { bar: undefined } foo、let a [b, ...undefined]、[a undefined] b模块导入import undefined from foo、import * as undefined from foo、import { undefined } from foo、import { a as undefined } from foo。可见规则的判定标准非常明确只有把undefined当作标识符即作为变量名被引用或被声明时才会触发把它当作普通属性名、键名、方法名使用是完全安全的。配置方式与选项no-undefined没有选项schema: []只能通过规则的开启/关闭状态和严重级别来配置{ rules: { no-undefined: error } }在 ESLint 9.x 的 flat config 下同样简单// eslint.config.js export default [ { rules: { no-undefined: error, // 或 warn / off }, }, ];由于规则不提供任何参数你唯一需要决定的就是严重级别off关闭、warn警告或error错误。需要说明的是该规则不在eslint:recommended中recommended: false必须显式启用才会生效同时它不可自动修复报告后需要人工改写代码。替代方案四规则协同守卫undefined规则文档明确指出如果不想全面禁用undefined可以换一个思路——不禁止使用而是禁止它被遮蔽或篡改确保undefined永远保持其原始语义。为此 ESLint 提供了两条配套规则文档中以相对链接的形式给出了指引对应仓库中的规则页为 docs/src/rules/no-global-assign.md 和 docs/src/rules/no-shadow-restricted-names.md规则作用与no-undefined的关系no-global-assign禁止对全局对象属性含undefined、NaN、Infinity等赋值防止undefined 1这类篡改全局undefined的写法no-shadow-restricted-names禁止遮蔽undefined、NaN、Infinity、eval、arguments等受限名称防止const undefined foo这类局部遮蔽其中no-shadow-restricted-names明确将undefined列为受限名称见 docs/src/rules/no-shadow-restricted-names.mdconst undefined foo、import { undefined } from bar都属于违规它甚至允许一种例外——let undefined;仅声明、从不赋值的遮蔽不被报告。此外围绕不要写undefined这一主题还有两条互补规则值得一并了解二者均为no-undefined的关联规则见文档 frontmatter 的related_rulesno-undef-initdocs/src/rules/no-undef-init.md源码 lib/rules/no-undef-init.js禁止用undefined初始化变量这种冗余写法let foo undefined;应写成let foo;。与no-undefined不同它是可自动修复的fixable: code并且会在undefined被遮蔽时自动跳过通过undefinedVar.defs.length 0判断避免误报no-voiddocs/src/rules/no-void.md源码 lib/rules/no-void.js方向完全相反——禁止使用void运算符其报错信息很有意思Expected undefined and instead saw void.。它支持allowAsStatement选项可放行作为独立语句的void表达式。需要注意no-undefined与no-void之间的张力no-undefined推荐用void 0替代undefined而no-void又禁止void运算符。二者同开时你既不能写undefined也不能写void 0此时唯一可行的方案是变量不初始化 typeof判断。是否同开取决于团队对void运算符的态度——如果只求规避遮蔽风险no-shadow-restricted-namesno-global-assign的保护undefined路线往往比完全禁用它更贴合多数代码库的实际情况。When Not To Use It何时关闭这条规则规则文档给出的关闭条件非常直接If you want to allow the use ofundefinedin your code, then you can safely turn this rule off.也就是说如果项目不介意使用undefined直接no-undefined: off即可关闭它不会有任何副作用。实践中常见的关闭理由包括代码库已通过no-shadow-restricted-names与no-global-assign保证了undefined语义不被破坏因而认为全面禁用过于严格团队风格偏好可读性更直白的x undefined判断当然此时需注意变量未声明时抛ReferenceError的风险与同样开启的no-void冲突导致void 0与undefined均不可用。小结no-undefined是一条零配置、无自动修复、行为冻结的suggestion规则它的核心价值在于从标识符层面彻底排除undefined被遮蔽或误读的可能文档主张用不初始化 typeofvoid 0三件套替代所有直接使用undefined的场景源码则通过作用域链遍历对每个作用域内undefined变量的所有定义defs与非初始化引用references逐一报告且精确避免了初始化处的重复计数。测试套件tests/lib/rules/no-undefined.js进一步锁定了属性名/键名/重命名导入导出不受影响、标识符引用与声明一律拦截的判定边界。如果项目希望保留undefined的书写自由可改用no-shadow-restricted-names与no-global-assign从根源上保护undefined的语义若希望进一步收紧风格还可叠加no-undef-init消除冗余初始化。最终取舍取决于你的团队更信任禁用它还是保护好它这两条截然不同的防御路线。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门