十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

eslint-plugin-unicorn 的 no-array-sort 规则:用 `ArraytoSorted()` 取代会原地修改数组的 `Arraysort()`

eslint-plugin-unicorn 的 no-array-sort 规则:用 `ArraytoSorted()` 取代会原地修改数组的 `Arraysort()` eslint-plugin-unicorn 的 no-array-sort 规则用Array#toSorted()取代会原地修改数组的Array#sort()【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读no-array-sort是 eslint-plugin-unicorn 中一条「偏好prefer」类规则核心使命是引导开发者用不会修改原数组的Array#toSorted()替代会原地排序的Array#sort()从而规避排序产生的隐式副作用。本文以该规则的官方文档为主体结合其规则源码、共享实现与测试用例完整讲解规则的触发条件、allowExpressionStatement选项的语义、编辑器建议修复的工作原理以及它在recommended/unopinionated配置中的启用情况帮助你在实际项目中正确配置并使用这条规则。为什么推荐toSorted()而不是sort()Array#sort()与Array#toSorted()的差异只有一点却至关重要Array#sort()会对原数组进行原地in-place修改排序完成后原数组的顺序已经被改变Array#toSorted()则返回一个新的已排序数组原数组保持不变。对于需要保留原始数据、或者基于同一份数据做多次不同处理的代码而言sort()的副作用往往是隐性 bug 的来源调用处看似只是「取一个排序结果」实际却污染了共享的数据结构。no-array-sort规则存在的意义就是让这种副作用在代码审查阶段被自动拦截。该规则在仓库中的定位十分明确docs/rules/no-array-sort.md开头即注明其行为是 “PreferArray#toSorted()overArray#sort()”并且已被收录进recommended与unopinionated两套预设配置意味着它是项目作者强烈推荐的默认开启规则之一见 readme.md 的规则总表。规则的报错与修复示例文档给出了三类最常见的违规模式这里逐一说明。通过展开运算符复制后再排序// ❌ 先复制再排序白白多写一次展开且 sort 是原地操作 const sorted [...array].sort(); // ✅ 直接调用 toSorted返回新数组 const sorted array.toSorted();对可迭代对象展开后排序// ❌ const sorted [...iterable].sort(); // ✅ const sorted [...iterable].toSorted();带比较函数的排序// ❌ const sorted [...array].sort((a, b) a - b); // ✅ const sorted array.toSorted((a, b) a - b);toSorted()与sort()接受完全相同的可选比较函数参数因此上述改写是纯机械替换不改变任何排序语义。深入源码规则到底在检查什么no-array-sort的实现非常精简——规则入口仅有几行import noArrayMutateRule from ./shared/no-array-mutate-rule.js; const config noArrayMutateRule(sort); export default config;它直接复用了共享工厂函数noArrayMutateRule该工厂同时服务于sort与reverse两条规则另一条为 no-array-reverse。从共享实现可以看到sort对应的匹配谓词predicate要求同时满足是一个方法调用方法名为sort最多只能有一个参数maximumArguments: 1不能是可选调用optionalCall: false即array.sort?.()这类不会被命中如果传了参数该参数必须是一个函数——isNodeValueNotFunction检查会排除掉非函数的参数。第 4 点非常关键它保证了规则只针对真正的Array#sort()数组排序而不会误伤其他 API。例如 Mongo 风格的查询对象collection.sort({field: 1})、query.sort(field)、query.sort(-1)等虽然方法名也叫sort但它们并不是数组的排序方法。这一点在测试用例中被系统性地覆盖验证sorted collection.sort({field: 1}), // valid参数不是比较函数 sorted query.sort(field), // valid sorted query.sort(1), // valid sorted query.sort(-1), // valid sorted query.sort(field), // valid sorted query.sort([criteria]), // valid [...array].sort({field: 1}), // valid反之参数为箭头函数或函数引用时会被判定为违规sorted array.sort(compareFn), // invalid sorted [...array].sort(compareFn), // invalid选项allowExpressionStatement默认放行「仅排序、不使用结果」的写法参数说明参数类型默认值说明allowExpressionStatementbooleantrue允许array.sort()以表达式语句即不接收返回值、单独成行的形式出现默认配置下下面的写法是合法的因为排序结果没有被使用属于「只关心副作用」的场景例如明确需要原地排序某个数组array.sort();但在传入allowExpressionStatement: false之后即使是独立的表达式语句也会被报告/* eslint unicorn/no-array-sort: [error, {allowExpressionStatement: false}] */ // ❌ 即使不接收返回值也会被禁止 array.sort(); // ✅ 显式接收新数组 const sortedArray array.toSorted();源码中的判定逻辑该选项的实现位于共享实现的create函数中。规则先检查调用表达式然后判断调用是否为「表达式语句上下文」当allowExpressionStatement为true且代码不是「展开数组后立即排序」[...array].sort()这种模式时如果调用的父节点是ExpressionStatement需要考虑可选链包装产生的ChainExpression则直接放行返回不产生任何诊断。换句话说规则的默认立场是「原地排序某个已存在的数组」作为一种有意识的副作用操作是被容忍的但「复制数组 排序」这种冗余且带副作用的模式无论选项如何都会被报告。测试中也验证了这一点test/no-array-sort.js即使设置allowExpressionStatement: false[...array].sort()依然会被判定为 invalid——因为它用展开运算符复制了数组却仍然原地排序属于明显的无意义复制。同时context.options[0]的解构配合defaultOptions: [{allowExpressionStatement: true}]共享实现保证了用户未显式传选项时行为始终一致。编辑器建议修复两种可选的改法该规则不会自动修复meta 中hasSuggestions: true规则头标注为 “manually fixable by editor suggestions”而是通过 ESLint 的 suggestion 机制提供两种可选改法由开发者在编辑器里手动确认仅替换方法名把.sort()直接改为.toSorted()。这是针对非展开场景如array.sort()以及展开场景中「希望保留复制行为」的默认建议消息为 “Switch to.toSorted().”。去掉多余的展开并调用toSorted()当代码形如[...array].sort()时规则识别出调用对象是一个仅含单个SpreadElement的数组表达式共享实现此时提供「展开对象本身就是一个数组」的建议——直接删除展开复制将内部参数提升为.toSorted()的接收者例如// 原代码 const sorted [...array].sort(); // 建议 A[...array].toSorted()保留复制 // 建议 Barray.toSorted()去掉复制语义等价于 toSorted 的新数组返回源码中还处理了一个细节当被展开的表达式优先级较低例如[...a b]时提升为方法接收者前会自动补上括号共享实现避免改写后产生解析错误——[...a b].sort()会安全地变为(a b).toSorted()。对应场景同样有测试覆盖test/no-array-sort.js。结合类型信息的智能跳过为了让规则更精准共享实现在「廉价检查」之后还会调用shouldSkipKnownNonArrayReceiver共享实现如果被调用的接收者通过类型信息可以确定既不是数组也不是类数组例如Setnumber类型的变量就跳过不报告。测试中的典型场景是// validfoo 的类型是 Setnumber调用 sort 不可能是数组排序 function f(foo: Setnumber) { foo.sort(); }不过正如源码注释所写解析接收者类型成本较高因此该检查被刻意放在所有快速检查之后执行只有前面全部通过才进入类型推断从而把性能开销降到最低。完整示例配置文件中的推荐写法由于规则已内置在recommended与unopinionated配置中你通常无需手动添加。若需要自定义选项可以在 ESLint 配置中显式覆盖// eslint.config.jsflat config 风格 export default [ // ...其他配置 { rules: { unicorn/no-array-sort: [error, {allowExpressionStatement: false}], }, }, ];选项对象只接受allowExpressionStatement这一个属性schema 中additionalProperties: false共享实现传入未知键会被 ESLint 判定为配置错误。规则的meta.type为suggestion适合在代码评审与 CI 阶段拦截而不是作为影响程序行为的运行时约束。相关规则与生态位置no-array-sort与 no-array-reverse 是「镜像」关系后者用Array#toReversed()取代Array#reverse()两者共用同一套noArrayMutateRule工厂与几乎相同的测试骨架只是替换的方法名和参数约束不同reverse要求 0 个参数而sort允许最多 1 个参数。另外仓库中还有一条相邻规则 no-array-sort-for-min-max用于禁止「通过排序数组来求最大/最小值」的低效写法如[...nums].sort((a, b) a - b)[0]它与本规则关注点不同但主题相近常被一起讨论。如果你想了解该规则更完整的合法/非法行为边界可以直接阅读其测试快照文件其中覆盖了可选链array?.sort()、空参数展开array.sort(...[])、多参数array.sort(compareFn, extraArgument)等大量边界情况是对文档示例最详尽的补充。小结no-array-sort用不可变方法toSorted()取代会原地修改数组的sort()从根本上消除排序副作用默认允许array.sort()作为独立表达式语句存在可通过allowExpressionStatement: false收紧规则通过「最多 1 个参数且参数必须是函数」的谓词精确识别真正的数组排序不会误伤 MongoDB、ORM 等同名sort方法不自动修复但提供编辑器建议既可仅替换方法名也可在[...array].sort()场景下一步到位地删除冗余复制已内置在recommended与unopinionated配置中开箱即用是让代码更安全、更现代的最小成本改造。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表