點選一鍵訂閱《雲薦大咖》專欄,獲取官方推薦精品內容,學技術不迷路!
可讀的程式碼能極大的提高開發效率。在開發的過程中,有很大一部分時間是在閱讀程式碼。可讀的程式碼,容易理解,也容易改。反之,不可讀性的程式碼,讀起來心情很差,改起來也容易出錯。
下面是一段不可讀讀的程式碼:
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 的規則。
程式碼質量的下一層次就是:可複用的程式碼,將會在下一篇文章中介紹。
金偉強往期精彩文章推薦:
聊聊程式碼質量 - 《學得會,抄得走的提升前端程式碼質量方法》前言
程式碼質量第 5 層 - 只是實現了功能**
程式碼質量第 4 層 - 健壯的程式碼
《雲薦大咖》是騰訊雲加社群精品內容專欄。雲薦官特邀行業佼者,聚焦於前沿技術的落地及理論實踐之上,持續為您解讀雲時代熱點技術、探索行業發展新機。點選一鍵訂閱,我們將為你定期推送精品內容。