
ESLint no-return-assign 規則詳解杜絕 return 語句中的賦值歧義【免費下載鏈接】eslintFind and fix problems in your JavaScript code.項目地址: https://gitcode.com/GitHub_Trending/es/eslint本指南以 ESLint 內置規則no-return-assign為核心講解為什么return語句中的賦值表達式是代碼異味以及該規則如何通過except-parens與always兩種模式幫你消除這種歧義。讀完本文你將掌握該規則的完整配置方法、父子選項的差異、與箭頭函數相關的特殊行為以及底層源碼的實現原理和測試驗證方式。背景return中的賦值為何危險JavaScript 的一大特點是賦值幾乎可以出現在任何表達式位置。正因如此一個多寫或少寫的等號很容易在真正想比較時卻執行了賦值這在return語句中尤為常見。例如function doSomething() { return foo bar 2; }此處return的意圖難以判斷可能是想返回bar 2的計算結果但為何又要把它賦給foo也可能是本意是使用比較運算符如這段代碼其實是個錯誤。由于這種歧義的存在社區公認的最佳實踐是不要在return語句中使用賦值表達式。no-return-assign規則正是為了實現這一最佳實踐而存在的。該規則從 ESLint 早期版本本倉庫數據 docs/src/_data/rule_versions.json 顯示其引入版本為0.0.9一直保留至今屬于suggestion建議類規則默認不開啟recommended: false見 docs/src/_data/rules.json 與 lib/rules/no-return-assign.js。規則詳情捕獲范圍與上報方式規則的目標是消除return語句中的賦值只要在return的返回值表達式或箭頭函數的表達式體中發現賦值就會給出警告。需要注意的是規則關注的是返回值表達式本身中的賦值。若賦值出現在return之后嵌套的函數體內則不會被這條規則誤傷。例如function x() { return function y() { result a * b }; // 正確賦值在嵌套函數體內不屬于外層 return 的返回值 }這條規則對應的報告消息有兩條定義于 lib/rules/no-return-assign.js 的meta.messagesmessageId消息文本觸發場景returnAssignmentReturn statement should not contain assignment.普通函數/塊體內的return返回了賦值arrowAssignmentArrow function should not return assignment.箭頭函數以表達式體直接返回賦值選項配置規則接受一個字符串選項取值必須為以下兩者之一對應 lib/rules/no-return-assign.js 中schema的enum: [except-parens, always]選項值含義默認值except-parens禁止賦值除非賦值被括號包裹? 默認always禁止一切賦值括號包裹也不行?由于defaultOptions: [except-parens]見源碼 lib/rules/no-return-assign.js 與 docs/src/_data/rules_meta.json在eslint.config.jsflat config中可以這樣顯式配置export default [ { files: [**/*.js], rules: { no-return-assign: error, // 使用默認 except-parens no-return-assign: [error, except-parens], // 顯式寫清默認值 no-return-assign: [error, always], // 最嚴格模式 }, }, ];選項一except-parens默認這是默認選項。它禁止在return中使用賦值除非整個賦值表達式被括號包圍。括號在這里充當我是有意的顯式聲明告訴讀者沒錯我就是要返回一個賦值表達式的結果。默認選項下的錯誤示例/*eslint no-return-assign: error*/ function doSomething() { return foo bar 2; } function doSomethingElse() { return foo 2; } const foo (a, b) a b const bar (a, b, c) (a b, c b) function doSomethingMore() { return foo bar foo 0; }以上全部違規前兩者是普通函數return直接返回賦值const foo (a, b) a b是箭頭函數表達式體直接返回賦值const bar (a, b, c) (a b, c b)中a b只是被逗號表達式包裹整個表達式并未被括號整體包圍因此仍然違規最后一個return foo bar foo 0;中的賦值出現在邏輯表達式中也未整體加括號違規。默認選項下的正確示例/*eslint no-return-assign: error*/ function doSomething() { return foo bar 2; } function doSomethingElse() { return foo bar 2; } function doSomethingMore() { return (foo bar 2); } const foo (a, b) (a b) const bar (a, b, c) ((a b), c b) function doAnotherThing() { return (foo bar) foo 0; }正確示例的要點/是比較運算而非賦值永遠合法return (foo bar 2);整個賦值表達式被括號包圍合法箭頭函數const foo (a, b) (a b)的表達式體整體帶括號合法((a b), c b)中賦值與整個逗號表達式都被括號包圍合法(foo bar) foo 0中只有賦值部分被括號包圍邏輯與表達式本身不在括號內——但規則只要求賦值表達式自身被括號包裹因此合法這點與上面(a b, c b)違規形成鮮明對比。選項二alwaysalways選項禁止return語句中一切賦值括號包裹也不能豁免。所有賦值都被視為問題。always 選項下的錯誤示例/*eslint no-return-assign: [error, always]*/ function doSomething() { return foo bar 2; } function doSomethingElse() { return foo 2; } function doSomethingMore() { return (foo bar 2); }注意最后一條在except-parens下合法的return (foo bar 2);在always下同樣違規。always 選項下的正確示例/*eslint no-return-assign: [error, always]*/ function doSomething() { return foo bar 2; } function doSomethingElse() { return foo bar 2; }在always模式下唯一安全的是比較運算。若確有先賦值再返回該值的需求應把賦值拆到return之前的語句中function doSomething() { const result bar 2; return result; }與相關規則的邊界no-return-assign只關心return/ 箭頭函數表達式體中的賦值若項目還需要管控其他位置的賦值歧義可在本倉庫 lib/rules 中尋找配套規則no-cond-assign禁止條件判斷如if、while中的賦值no-assign-ternary禁止三元表達式分支中的賦值該規則在本倉庫中為實驗性規則需開啟實驗性 flag。三者互補可以覆蓋賦值表達式在常見語法位置的全部隱患。何時不使用此規則如果你希望允許在return語句中使用賦值運算符例如團隊代碼風格依賴return x y這種緊湊寫法或某些庫的回調模式需要返回賦值結果可以放心關閉此規則export default [ { rules: { no-return-assign: off, }, }, ];關閉后規則不產生任何檢查也不會影響其他規則工作。源碼剖析規則如何判定括號包裹與祖先鏈閱讀 lib/rules/no-return-assign.js 的create(context)實現可以完整還原規則的判定邏輯。第一步括號豁免檢查規則監聽每一個AssignmentExpression節點。若選項不是except-parens即always直接跳過括號判斷否則調用工具函數astUtils.isParenthesised(sourceCode, node)const always context.options[0] ! except-parens; ... AssignmentExpression(node) { if (!always astUtils.isParenthesised(sourceCode, node)) { return; } ... }isParenthesised的實現位于 lib/rules/utils/ast-utils.js它通過比較賦值表達式節點前后 token 是否為(/)并校驗括號的 range 是否緊貼節點邊界從而精確判定該節點是否被括號整體包裹——這正是前面只有賦值本身被括號包圍才放行的機制來源。這也是為什么(result) (a * b)這種只在標識符兩側加括號的寫法仍然違規括號沒有包住整個賦值節點。第二步沿祖先鏈尋找哨兵節點跳過括號檢查后規則用一個SENTINEL_TYPE正則沿祖先鏈向上查找const SENTINEL_TYPE /^(?:[a-zA-Z]?Statement|ArrowFunctionExpression|FunctionExpression|ClassExpression)$/u;從當前賦值節點出發逐個向上遍歷父節點直到遇到第一個哨兵——即任意*Statement語句節點如ExpressionStatement、ReturnStatement或函數/類表達式。這樣做的目的是定位賦值所屬的最近語句邊界避免跨越函數邊界誤報比如把內層函數體內的賦值算到外層return頭上。第三步分場景上報若祖先鏈盡頭是ReturnStatement則報returnAssignment若盡頭是ArrowFunctionExpression且該賦值節點正是箭頭函數表達式體parent.body currentChild則報arrowAssignment若盡頭是FunctionExpression/ClassExpression或普通語句說明賦值不在return路徑上不報告。這條實現路徑解釋了為何return foo () a返回一個箭頭函數的同時對foo賦值報returnAssignment而const foo (a) (b) a b柯里化箭頭函數的最內層表達式體直接返回賦值報arrowAssignment。測試驗證規則行為的完整清單規則行為由 tests/lib/rules/no-return-assign.js 通過RuleTester全面覆蓋測試用例與官方文檔示例一一對應并補充了大量邊界場景valid不報錯關鍵用例包括function x() { return (result a * b); }默認與except-parens下合法() { return (result a * b); }與() (result a * b)塊體加括號與表達式體加括號均合法const foo (a,b,c) ((a b), c)整個逗號表達式加括號合法function x() { return function y() { result a * b }; }嵌套函數體中的賦值不受外層return牽連const foo (a) (b) (a b)多層箭頭函數逐層加括號合法module.exports {a: 1};頂層賦值不涉及return合法。invalid報錯關鍵用例包括function x() { return result a * b; }→returnAssignmentfunction x() { return (result) (a * b); };→returnAssignment括號未包住整個賦值節點默認模式也報錯() result a * b→arrowAssignmentreturn result || (result a * b);always下邏輯表達式中的賦值也報錯return foo function(){ return (bar bar1) }→returnAssignmentreturn () a () b→arrowAssignment返回的箭頭函數表達式體含賦值const foo (a) (b) a b→arrowAssignment多層箭頭函數最內層未加括號。這些用例同時驗證了規則對ecmaVersion: 6及以上語法箭頭函數的支持以及languageOptions配置在RuleTester中的正確用法。小結no-return-assign是一條簡單但高頻實用的建議類規則默認except-parens模式下它要求任何出現在return/ 箭頭函數表達式體中的賦值都必須用括號顯式聲明意圖always模式則徹底禁止此類寫法倒逼開發者把賦值拆分為獨立語句。結合本倉庫中的源碼實現lib/rules/no-return-assign.js、工具函數lib/rules/utils/ast-utils.js與完整測試tests/lib/rules/no-return-assign.js你可以精確預判該規則在任意賦值形態下的判定結果并據此決定是開啟、加嚴還是關閉它。【免費下載鏈接】eslintFind and fix problems in your JavaScript code.項目地址: https://gitcode.com/GitHub_Trending/es/eslint創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考