ESLint 规则 no-array-constructor 深度解析:为什么数组应使用字面量写法
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
本篇文章围绕 ESLint 内置规则no-array-constructor展开,讲解它为何禁止使用Array构造函数创建数组、在何种场景下允许例外,以及它如何支持 JavaScript 与 TypeScript 语法。读完本文,你将完整掌握该规则的判定逻辑、可自动修复的行为边界,以及何时应关闭此规则。
规则背景:为什么Array构造函数被普遍反对
在 JavaScript 中,Array构造函数可以通过多种方式创建数组,但社区普遍更倾向于使用数组字面量([])写法,原因主要有两个:
- 单参数陷阱:
Array构造函数只接收一个数字参数时,不会创建一个包含该数字的数组,而是创建一个长度为该数字的空数组(稀疏数组)。例如Array(3)创建的是长度为 3 的稀疏数组,而不是[3]。这种语义极易引发困惑和隐藏 bug。 Array全局对象可能被重新定义:在全局作用域中,Array标识符可以被赋值或遮蔽,直接使用new Array()可能调用到的是被改写过的实现。
唯一的例外场景是:刻意使用单个数字参数来创建指定长度的稀疏数组,这是Array构造函数独有的、字面量无法直接等价替代的能力。因此规则允许Array(500)、new Array(someArray.length)这类用法,而字面量[500]与Array(500)的语义完全不同,绝不可互相替换。
规则详情
该规则禁止使用Array构造函数(包括new Array()调用形式与直接调用形式Array()),但放行上面提到的单数字参数稀疏数组场景。
不正确的代码示例
/*eslint no-array-constructor: "error"*/ Array(); Array(0, 1, 2); new Array(0, 1, 2); Array(...args);以上代码都会触发no-array-constructor报错,其中Array(...args)这种展开参数的形式同样在禁止之列(详见下文源码解析中对SpreadElement的处理)。
正确的代码示例
/*eslint no-array-constructor: "error"*/ Array(500); new Array(someOtherArray.length); [0, 1, 2]; const createArray = Array => new Array();前两行利用了规则的“单个数字参数”例外;[0, 1, 2]是推荐的字面量写法;最后一行中Array被局部函数参数遮蔽,不再指向全局Array构造函数,因此不触发规则(这一点与源码中的全局变量判定逻辑一致,见下文)。
TypeScript 语法支持
该规则额外支持 TypeScript 类型语法。由于 TypeScript 中Array<Foo>常用于类型标注而非构造调用,规则对此进行了区分。
正确的 TypeScript 代码示例
/*eslint no-array-constructor: "error"*/ new Array<number>(1, 2, 3); new Array<Foo>(); Array<number>(1, 2, 3); Array<Foo>(); Array?.foo();带类型参数的Array<Foo>会被视为类型引用而非数组构造,因此放行;Array?.foo()是访问Array上的属性方法,也不属于构造调用。
不正确的 TypeScript 代码示例
/*eslint no-array-constructor: "error"*/ new Array(); new Array(0, 1, 2); Array?.(x, y); Array?.(0, 1, 2);无类型参数的构造调用(即使带有可选链?.)依然会被报告。关于可选链的处理细节,请参考 源码实现 与 TypeScript 测试用例。
源码实现:规则到底如何判定
该规则对应的核心实现位于 lib/rules/no-array-constructor.js,规则的元信息声明如下:
- 规则类型(
meta.type)为suggestion,与文档 front matter 中的rule_type: suggestion一致; - 声明了
fixable: "code"与hasSuggestions: true,说明它既支持直接自动修复,也支持提供修复建议; schema: [],表示该规则不接受任何配置选项;- 监听
CallExpression(Array()形式)与NewExpression(new Array()形式)两类 AST 节点,见 lib/rules/no-array-constructor.js。
判定核心逻辑(check函数)
从 check 函数 可以看到,命中报错需要同时满足三个条件:
- 调用对象必须是名为
Array的裸标识符:node.callee.type !== "Identifier" || node.callee.name !== "Array"时直接返回,因此foo.Array()、globalThis.Array、new Array.foo等成员访问形式不会被报告; - 不得携带类型参数:
node.typeArguments存在时直接返回,这正是 TypeScript 中Array<Foo>放行的原因; - 不得是单个非展开参数:
node.arguments.length === 1 && node.arguments[0].type !== "SpreadElement"时直接返回,这就是“单数字参数创建稀疏数组”例外在源码中的落点;而Array(...args)是SpreadElement,不满足此条件,所以会被报告。
此外,规则还会调用 getVariableByName 沿作用域链向上查找名为Array的变量,并且只有在该变量是预定义全局变量(variable.identifiers.length === 0,即没有任何声明)时才报错。这就是上文中const createArray = Array => new Array()不报错的原因——局部参数遮蔽了全局Array。测试用例中的"var Array; new Array;"与关闭全局Array(globals: { Array: "off" })的场景也验证了这一点,见 tests/lib/rules/no-array-constructor.js。
自动修复与修复建议的分工
规则提供了两种修复途径(对应三条消息,见 源码 messages 定义):
preferLiteral:报告主体消息“The array literal notation [] is preferable.”;useLiteral/useLiteralAfterSemicolon:修复建议消息“Replace with an array literal.”(必要时补充前置分号)。
从源码看,fix(直接自动修复)与suggest(修复建议)是互斥的分工:
- 当不应建议(即
shouldSuggest为假)时,直接修复生效,将Array(...)替换为[...]字面量; - 当应建议时(
shouldSuggest为真),直接修复返回null,只提供修复建议供开发者手动应用。
shouldSuggest为真的三种情形(见 lib/rules/no-array-constructor.js):
- 节点带有可选链(
node.optional),例如Array?.(0, 1, 2)——修复会改变可选语义,不宜强制; - 参数数量大于 0 但非展开参数少于 2 个,例如
Array(...args)、Array(5, ...args)——修复为[...args]会改变调用语义,只给建议; Array构造函数表达式内存在注释——hasCommentsInArrayConstructor检测到注释时,为避免破坏注释而只给建议(测试中大量/*a*/Array()/*b*/场景验证了这一点)。
分号自动插入(ASI)的补偿处理
这是本规则最精巧的实现细节。JavaScript 的自动分号插入(ASI)规则下,Array()或new Array()出现在某些语句之后时无需前置分号;但一旦被替换为[...]字面量,[可能被解析为上一表达式的属性访问或其它延续,导致语义改变。
因此规则借助 needsPrecedingSemicolon 与 isStartOfExpressionStatement 判断:
- 若替换为
[...]后需要补充分号,则生成;[...]并报告useLiteralAfterSemicolon; - 否则生成
[...]并报告useLiteral。
例如测试中foo\nArray()会被修复为foo\n;[],而语句起始位置的Array()则直接修复为[]。相关的分号判断测试用例集中在 tests/lib/rules/no-array-constructor.js,覆盖了a++、++a、return、yield、export、import、JSX 等大量前后语句形态。
配置选项
此规则没有选项。该规则不接收任何配置参数(schema: []),只需指定严重级别即可启用,例如:
/*eslint no-array-constructor: "error"*/或在扁平配置中使用:
export default [ { rules: { "no-array-constructor": "error", }, }, ];需要注意的是,该规则并未包含在eslint:recommended预设中(规则元信息中recommended: false,同时可在 tests/conf/eslint-recommended.js 与 conf/rule-type-list.json 中确认其未列入推荐集合),需要手动开启。规则通过 lib/rules/index.js 中的懒加载映射注册到 ESLint 规则系统。
何时不使用此规则
该规则强制的是一个近乎普适的风格约定,绝大多数代码库都应遵循。但如果你或你的团队偏好构造函数写法(例如更习惯new Array()的显式性),完全可以关闭此规则。
此外,在需要刻意创建指定长度的稀疏数组的场景中,Array(n)是字面量无法替代的惯用法,规则本身已将其作为例外放行,无需担心误报。
相关规则
no-array-constructor在文档 front matter 中声明了关联规则:
- no-new-wrappers:禁止对
String、Number、Boolean等原始包装对象使用new; - no-object-constructor:禁止使用
Object构造函数(new Object()/Object())。
三者共同构成 ESLint 中“使用字面量而非构造函数”的一组相关约定。如果你想了解规则在真实代码上的全部行为边界(含注释、可选链、TypeScript 类型语法、ASI 分号补偿等),可以直接运行仓库中的测试:node --test tests/lib/rules/no-array-constructor.js。
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考