资讯动态

eslint-plugin-unicorn 规则详解:consistent-function-scoping 把函数提升到最高作用域

发布时间:2026/9/18 14:17:41 来源:尧图企业网站定制
eslint-plugin-unicorn 规则详解consistent-function-scoping 把函数提升到最高作用域【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn导读consistent-function-scoping是 eslint-plugin-unicorn 中一条在recommended配置里默认启用的规则核心目标是把函数定义移动到能捕获所需值的前提下尽可能高的作用域通常是最外层模块作用域从而提升可读性并让 JS 引擎更容易做优化。本文以仓库中的规则快照报告为主线结合规则文档、规则源码与测试用例完整讲解该规则检测哪些函数、输出什么样的错误消息、如何配置以及它刻意忽略哪些场景。规则做什么把函数放到它该待的最高层一条简单的原则函数定义应尽量靠近顶层作用域前提是不能破坏它捕获的值。这带来两个实际收益可读性函数定义集中在模块顶层读者一眼能看到模块提供了哪些能力而不是在函数嵌套里翻找性能每次外层函数被调用内层函数都会被重新创建移出后函数对象只创建一次同时引擎能更好地优化这类热函数。快照报告中的 8 个 invalid 用例恰好把这条规则能捕获的函数形态完整地列了出来是理解规则行为最直观的入口。快照解读规则检测哪 8 种放错位置的函数快照报告 test/snapshots/consistent-function-scoping.js.md 由 AVA 在运行test.snapshot()见 test/consistent-function-scoping.js时生成对应的invalid输入逐条展示了规则的错误输出格式。逐一来看1. 嵌套普通函数声明function foo() { function bar() {} }输出 2 | function bar() {} | ^^^^^^^^^^^^ Move function bar to the outer scope.错误定位到bar的整个函数头消息为Move function bar to the outer scope.。2. 嵌套 async 函数声明function foo() { async function bar() {} }消息变为Move async function bar to the outer scope.函数种类被精确写进了错误消息。3. 嵌套 generator 函数声明function foo() { function * bar() {} }消息为Move generator function bar to the outer scope.。4. 嵌套 async generator 函数声明function foo() { async function * bar() {} }消息为Move async generator function bar to the outer scope.。function/async function/function */async function *四种声明形态全覆盖。5. 用 const 声明的箭头函数function foo() { const bar () {} }注意错误的下划线只标记在上源码中通过getFunctionHeadLocation定位函数头消息为Move arrow function bar to the outer scope.。6. return 语句中的嵌套箭头函数const doFoo () bar bar;消息为Move arrow function to the outer scope.—— 这个匿名箭头函数直接写在return里没有名字所以错误消息省略了函数名。源码中getParentNodeAfterNestedArrowFunctions专门处理return 语句后跟着一串嵌套箭头函数的情况保证只报告真正能外移的内层函数。7. const 声明的 async 箭头函数function foo() { const bar async () {} }消息为Move async arrow function bar to the outer scope.。8. const 声明的函数表达式function doFoo() { const doBar function(bar) { return bar; }; }函数表达式同样被检测错误标记落在function关键字上消息为Move function doBar to the outer scope.。注意与箭头函数不同普通函数表达式在return语句中不会被标记见源码 rules/consistent-function-scoping.js 的注释普通函数表达式语义不同外移可能改变this/arguments绑定因此只有 return 中的箭头函数才会被跳过处理逻辑。从源码看这些函数种类全部来自functionTypes见 rules/ast/index.js 导出的 function-types.js规则在 AST 遍历退出这些节点时触发检查context.onExit(functionTypes, ...)见 rules/consistent-function-scoping.js。什么样才叫可以外移基于 scope 的引用分析规则不会盲目报告所有嵌套函数——只有当函数没有捕获外层作用域的任何东西时才认为它可以提升。这依赖于 ESLint 的scopeManager做引用分析核心实现在 checkReferences收集函数作用域内所有引用getReferences会连同所有子作用域的引用一起展平见 get-references.js只要任一引用解析到的定义/作用域落在父作用域集合parentScopes里就说明函数捕获了外层变量不能外移对递归函数名definition.type FunctionName且名字与函数自身一致做特殊跳过避免把递归误判为捕获外部变量对相邻函数声明做额外检查hitIdentifier处理兄弟函数之间的引用关系。对应的 valid 测试用例能帮你快速建立直觉见 test/consistent-function-scoping.js// ✅ 捕获了参数 foo不能外移 function doFoo(foo) { function doBar(bar) { return foo bar; } return foo; } // ✅ 捕获了块级变量 foo不能外移 function doFoo() { const foo foo; function doBar(bar) { return foo bar; } return foo; }而完全不捕获任何外层值的函数即使只是躺在函数里从未使用也会被报告见快照用例 1-8 以及测试中的function doFoo() { function doBar() {} }等 invalid 用例。嵌套箭头函数链与循环体两个容易被误报的场景快照用例 6 只是冰山一角源码里还有两处专门处理复杂嵌套的逻辑返回语句中的箭头函数链return next action {...}这种柯里化写法在 Redux middleware 中很常见。规则通过getParentNodeAfterNestedArrowFunctions/skipArrowFunctionChain正确处理从 ReturnStatement 出发穿过一串箭头函数的情况只标记真正不再捕获外部值的层。测试中的export function middleware(store) { return next action {...} }捕获store是 valid而function middleware() { return next action {...} }不捕获任何外部值会报两个错误见 test/consistent-function-scoping.js。循环体内的函数for/while/do...while循环体内的块级作用域与循环语句作用域是分开的。getRelevantParentScopesrules/consistent-function-scoping.js会把循环语句自身的作用域也加入parentScopes一并比较从而正确判断循环内定义、只捕获循环变量的函数不能外移因为外移后循环变量就没了。测试中用了一整组 valid 用例覆盖for...of/for...in/for(;;)/while/do...while及嵌套块的各种组合test/consistent-function-scoping.js对应的const match () true;不捕获循环变量、可以外移的用例则是 invalidtest/consistent-function-scoping.js。配置选项checkArrowFunctions规则的 JSON Schema 定义在 rules/consistent-function-scoping.js只有一个选项选项类型默认值说明checkArrowFunctionsbooleantrue是否检查箭头函数配置示例关闭对箭头函数的检查只检查普通函数声明/表达式{ rules: { unicorn/consistent-function-scoping: [error, {checkArrowFunctions: false}] } }关闭后function outer() { const inner () {} }不再报错但function outer() { function inner() {} }仍然会被报告——测试用例专门验证了这一行为test/consistent-function-scoping.js 与 test/consistent-function-scoping.js。defaultOptions中的默认值保证不传选项时行为一致rules/consistent-function-scoping.js。规则刻意忽略的四类场景文档 Limitations 一节和源码共同确认了以下误报豁免区了解它们能避免你误以为规则有 bug1. 函数内多余的花括号代码块function doFoo(foo) { { function doBar(bar) { return bar; } } return foo; }规则不检测也不移除函数内部多余代码块里的函数。注意这只针对额外嵌套的块直接嵌套在函数体里的函数仍会被检查对应 invalid 用例见 test/consistent-function-scoping.js。2. IIFE立即调用函数表达式(function () { function doFoo(bar) { return bar; } })();IIFE 内的函数被整体豁免因为 IIFE 本身就是一种作用域隔离手段。源码通过isIife判断节点是否为父节点是 CallExpression 且自身就是 callee的函数表达式/箭头函数rules/consistent-function-scoping.js。测试覆盖了(function(){...})()、(function(){...}())、!function(){...}()、(() {...})()、async IIFE、async generator IIFE 以及 IIFE 与非 IIFE 混在参数列表里的复杂用例test/consistent-function-scoping.js、test/consistent-function-scoping.js。注意 IIFE内部再嵌套的函数不受豁免仍会正常报告。3. React 内置 Hooks 的回调useEffect(() { async function getItems() {} getItems(); }, [])useState/useEffect/useContext/useReducer/useCallback/useMemo/useRef等 React 内置 Hooks含React.xxx前缀写法完整列表见 rules/consistent-function-scoping.js的回调作用域被豁免避免把 Hooks 惯用法误报为应该外移。同时NotReact.useEffect这类非 React 调用不会获得豁免对应 invalid 用例见 test/consistent-function-scoping.js。4. jest.mock 工厂函数jest.mock(module, () { function createMock() { return mock; } return createMock; });Jest 限制 mock 工厂不能引用作用域外的变量因此工厂内的函数即使看起来可以外移也必须留在原地。源码通过isInsideJestMockFactory向上遍历 AST匹配jest.mock/jest.doMock的第二个参数rules/consistent-function-scoping.js。测试还验证了jest.doMock(module, () () mock)这种工厂内直接返回箭头函数的内层函数仍会被报告test/consistent-function-scoping.js。this 与 arguments箭头函数的词法敏感检查箭头函数没有自己的this/arguments如果箭头函数体内引用了词法this或参数默认值引用了this把它外移会改变this的绑定语义。规则通过isNodeContainsLexicalThis配合 AST visitorKeys 做词法级检查而非依赖 parser 的scope.thisFound元数据对这类箭头函数直接跳过rules/consistent-function-scoping.js。对应测试用例// ✅ 捕获 this 的箭头函数不外移 function doFoo(Foo) { const doBar () this; return doBar(); }; // ✅ 多级嵌套箭头函数链中的 this 同样被识别 function doFoo(Foo) { const doBar () () () this; return doBar(); }; // ✅ 参数默认值引用 this 也会被识别 function doFoo(Foo) { const doBar (value this) value; return doBar(); };见 test/consistent-function-scoping.js。而引用arguments的普通函数声明会被正常报告它有自己的arguments对象外移不影响语义测试中function doBar() { return arguments; }是 invalidtest/consistent-function-scoping.js。实际修复示例把函数从嵌套中解放出来规则文档给出了正反对照的修复模式。最常见的坏味道是函数不捕获任何外层值却被定义在内部// ❌ 不捕获任何外部值却被包在 doFoo 里 export function doFoo(foo) { function doBar(bar) { return bar bar; } return doBar; } // ✅ 提到模块顶层 function doBar(bar) { return bar bar; } export function doFoo(foo) { return doBar; }箭头函数同理// ❌ export function someAction() { return dispatch dispatch({type: SOME_TYPE}); } // ✅ const handleDispatch dispatch dispatch({type: SOME_TYPE}); export function someAction() { return handleDispatch; }而确实捕获了外层值的函数比如捕获参数foo、使用foo.doBar的场景即使嵌套也保持原样规则不会误报对应文档末尾的 ✅ 示例。在项目中使用启用、验证与快照测试启用该规则属于recommended配置meta.docs.recommended: true见 rules/consistent-function-scoping.js并在 rules/index.js 中注册导出。使用recommended配置即默认开启unopinionated配置中该规则被禁用。手动验证仓库用 AVA 运行规则测试快照用例通过test.snapshot({invalid: [...]})生成test/consistent-function-scoping.js快照本体保存在test/snapshots/consistent-function-scoping.js.snap而其人类可读版即本文引用的 test/snapshots/consistent-function-scoping.js.md。如果你修改了规则行为需要运行对应的快照更新命令让快照同步。类型安全规则还附带 TypeScript 测试套件test.typescript(...)覆盖泛型箭头函数链、FinalizationRegistry回调中的this、this: unknown显式标注等 TS 场景test/consistent-function-scoping.js确认规则对 TS 语法同样安全。小结consistent-function-scoping通过 ESLint 作用域分析把不捕获外层值却被层层嵌套的函数精准识别出来并在错误消息中区分function、async function、generator、箭头函数等形态对应快照报告的 8 个代表性用例同时为 IIFE、React Hooks、jest.mock 工厂、词法this等合理场景设置了豁免。理解它背后的parentScopes引用分析、循环体作用域合并和嵌套箭头函数链处理你就能在启用该规则时准确预判报告结果写出函数就该待在它最高该待的位置的代码。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价