點擊一鍵訂閱《雲薦大咖》專欄,獲取官方推薦精品內容,學技術不迷路!
可讀的代碼能極大的提高開發效率。在開發的過程中,有很大一部分時間是在閱讀代碼。可讀的代碼,容易理解,也容易改。反之,不可讀性的代碼,讀起來心情很差,改起來也容易出錯。
下面是一段不可讀讀的代碼:
const user = ... const foo = (cb) => ... const bar = (list, cb) => ... const other = (list1, list2) => ... if(user ? user.isAdmin : ((user.permission && user.permission.view) ? user.permission.view === true : false)) { foo((res) => { if(res && res.ok && res.list && res.list.length > 0) { bar(res.list, (res2) => { if(res2 && res2.ok && res2.list && res2.list.length > 0) { other(res.list, res2.list) } }) } }) }
以上代碼有這些問題:
- 函數的命名和功能不符。
- if 條件太複雜,而且嵌套深。
- 回調函數嵌套深。
將上面的代碼改成可讀的代碼:
const user = ... const fetchChannel = () => ... const fetchArticle = (list) => ... const render = (channelList, articleList) => ... const hasViewPermission = user.isAdmin || user.permission?.view if(!hasViewPermission) { return } const { ok, list: channelList} = await fetchChannel() if (!(ok && channelList?.length > 0)) { return } const { ok: fetchArticleOk, articleList } = await fetchArticle(channelList) if (!(fetchArticleOk && articleList.length > 0)) { return } render(channelList, articleList)
總結來說,可讀的代碼主要有如下的特點:
- 一致的代碼風格。
- 合理的命名。
- 必要的註釋。
- 沒有大文件。
- 沒有嵌套很深的代碼
如何寫出可讀代碼?
寫出可讀代碼,要滿足上面提到的特點。
一、一致的代碼風格
一致的代碼風格指:空格,縮進,命名風格(駝峯,中劃線等)等在整個項目裏是一致的。一致的代碼風格,看起來很整齊,也能減少理解成本。在項目中,用怎樣的代碼風格不重要。重要的是,風格要一致。
前端業界比較有名的代碼風格有:Airbnb JavaScript Style Guide 和 JavaScript Standard Style。不想折騰的,可以使用 JavaScript Standard Style。JavaScript Standard Style 的特點:
- 無須配置。 史上最便捷的統一代碼風格的方式,輕鬆擁有。
- 自動代碼格式化。 只需運行
standard --fix
從此和髒亂差的代碼說再見。 - 提前發現風格及程序問題。 減少代碼審查過程中反反覆覆的修改過程,節約時間。
確定了代碼風格後,可以用檢查工具 ESLint 來保證代碼風格的統一。每次代碼提交前,做檢查,可以用工具:husky。對於大項目,檢查整個項目太慢。用 lint-staged 只檢查本次改動的文件。
二、合理的命名
There are only two hard things in Computer Science: cache invalidation and naming things. 計算機科學中只有兩件事很難:緩存失效和命名。 -- Phil Karlton
好的命名是“看其名而知其意”。具體來說,好的命名有如下特點。
直白的,有意義的
好的命名是易於理解的,也就是直白的,有意義的。比如:fetchUserInfo
。
推薦:故宮命名法
提取目標對象的關鍵特徵來命名。
推薦命名工具: CODELF。它幫你搜索 Github、GitLab 等網站中,你想查找的內容的不同命名。
注意,命名中的單詞不要拼錯。推薦單詞拼寫檢查工具:Code Spell Checker。
遵循行業慣例
好的命名也應該遵循行業的習慣慣例。如:業界慣例 id 作爲唯一標識命名,不要用 identifier。i、j、k 用來做循環計數變量命名。
符合代碼風格
好的命名應該符合當前項目的代碼風格。如:駝峯風格等。
不好的命名有如下特點:
無意義的名字
無意義的名字,如:foo, bar, var1, fun1。
太過抽象的名字
太過抽象的名字,如:data,res,temp,tools。
會有歧義的簡稱
會有歧義的簡稱,如:mod。你可能無法確認到底是 mode 或 module。
不必要的上下文信息
// bad function async getUserName (userId) { const userName = await fetchUser(userId) return userName } // good function async getUserName (id) { const name = await fetchUser(id) return name }
太長的名字
太長的名字,不容易理解。如:fetchGraintYoungestBoyName。優化方式:將不重要內容省略掉。如改成:fetchBoyName。
三、必要的註釋
註釋是是對代碼的解釋和說明。好的代碼是自我解釋的。對於不復雜的代碼,不需要註釋。如果寫的註釋,只是解釋了代碼做了什麼,不僅浪費讀者的時間,還會誤導讀者(註釋和代碼功能不一致時)。
需要寫註釋的場景:
- 當代碼本身無法清晰地闡述作者的意圖。
- 邏輯比較複雜。
四、沒有大文件
大文件指:代碼行數很多(超過1千行)的文件。大文件,意味代碼做了很多事,很難跟蹤到底發生了什麼。
可以用 ESLine 中 max-lines 規則來找出大文件。
優化方案:按功能,將大文件拆分成若干小文件。
五、沒有嵌套很深的代碼
嵌套很深的代碼,可讀性很差,讓人產生“敬畏感”。比如:
fetchData1(data1 => fetchData2(data2 => fetchData3(data3 => fetchData4(data4 => fetchData5(data5 => fetchData6(data6 => fetchData7(data7 => done(data1, data2, data3, dat4, data5, data6, data7) ) ) ) ) ) ) )
下面是幾種常見的嵌套很深的場景。
1.回調地獄
用回調函數的方式來處理多個串行的異步操作,會造成嵌套很深的情況。俗稱“回調地獄”。如:
fetchData1(data1 => fetchData2(data2 => fetchData3(data3 => done(data1, data2, data3) ) ) )
2.if 嵌套很深
在條件語句中,如果判斷條件很多,會出現嵌套很深或判斷條件很長的情況。比如,判斷一個值是否是: 1 到 100 之間,能被 3 和 5 整除的偶數。這麼寫:
const isEvenNum = num => Number.isInteger(num) && num % 2 === 0 const isBetween = num => num > 1 && num < 100 const isDivisible = num => num % 3 === 0 && num % 5 === 0 if (isEvenNum(num)) { // 是偶數 if(isBetween(num)) { // 1 到 100 之間 if(isDivisible(num)) { // 能被 3 和 5 整除 return true } return false } return false } return false
三元表達式也會出現嵌套很深的情況:
a > 0 ? (b < 5 > ? (c ? d : e) : (f ? g : h)) : (i ? j : k)
3.函數調用嵌套
執行多個函數調用,每個函數輸出是下個函數的輸入,會造成很深的嵌套。如:
// 模擬炒蛋的過程:買蛋 -> 打蛋 -> 炒蛋 -> 上桌。 toTable( // 第四步: 上桌 fry( // 第三步: 炒蛋 handle( // 第二步: 打蛋 buy(20) // 第一步: 買蛋 ) ) )
4.React 高階組件嵌套
在 React 寫的應用中,會出現一個組件被很多個高階組件(HOC)包裹,造成嵌套很深的情況。如:
class Comp extends React.Component {...} Wrapper5( Wrapper4( Wrapper3( Wrapper2( Wrapper1(Comp) ) ) ) )
5.React Context 嵌套
在 React 寫的應用中,可以用 Context 來管理子組件間的數據共享。如果共享數據很多,而且類型不同,容易造成頂部組件 Context 嵌套很深。如:
<PageContext.Provider value={...} > <User.Provider value={...} > <Project.Provider value={...} > <ChildComp /> </Project.Provider> </User.Provider> </PageContext.Provider>
優化方案見: 這裏。
總結
符合本文提到的可讀代碼特點的代碼,可讀性都不會差。當然,還有很多能提升代碼的可讀性的技巧。比如:
- 限制函數的參數數量。
- 限制函數的圈複雜度。
- 禁用
with
語句。
要了解更多提升代碼可讀性的技巧,推薦擼一遍 ESLint 的規則。
代碼質量的下一層次就是:可複用的代碼。我會在下一篇文章中介紹。
金偉強往期精彩文章推薦:
代碼質量第 4 層 - 健壯的代碼
代碼質量第 5 層 - 只是實現了功能
聊聊代碼質量 - 《學得會,抄得走的提升前端代碼質量方法》前言
《雲薦大咖》是騰訊雲加社區精品內容專欄。雲薦官特邀行業佼者,聚焦於前沿技術的落地及理論實踐之上,持續爲您解讀雲時代熱點技術、探索行業發展新機。點擊一鍵訂閱,我們將爲你定期推送精品內容。