什麼是可維護性的程式碼,這十六點得知道

千鋒Python唐小強發表於2020-08-04

什麼是可維護性的程式碼

今天我們不聊效能最佳化,只是從後期維護程式碼的角度談談如何優雅的書寫程式碼

  • 為什麼需要些可維護性高的程式碼 ?

在開發的過程中,迭代和維護是再正常不過的操作了
那麼就必然要閱讀別人的程式碼
你有沒有遇到過一些尷尬的事情:
1、看不懂別人的程式碼,不知從何下手
2、修改一個功能,得讀兩天程式碼,改完發現 bug 最少的時候是修改以前
3、只是修改了一行程式碼,發現控制檯報錯好幾十個
...
如果程式碼的可維護性高了,那麼可以避免很多這些問題
編寫可維護性高的程式碼, 從我做起 ^_^

  • 什麼是可維護性高的程式碼 ?

容易理解: 不需要求助原始碼書寫人員,就能看得懂
符合常識: 程式碼書寫的自然通透
容易適配: 當資料發生變化的時候,不至於完全重寫
容易擴充套件: 對於核心功能有可擴充套件性(適當利用策略模式)
容易除錯: 當出現問題的時候,能給出明確且詳細的錯誤提示,可以直接定位問題源

什麼是可維護性的程式碼,這十六點得知道

從下面幾點做起:

一、程式碼可讀性

  • 想要好維護, 那麼第一任務就是你寫的程式碼要讓別人看得懂
  • 因為我們的程式碼,當他不執行的時候,就是一個純文字
  • 想要讓別人看得懂你寫的一堆文字,那麼就要從一切自定義的內容開始做起

二、程式碼縮排

  • 能區分是論文還是程式碼的第一因素,也是最直觀的因素就是程式碼縮排
  • 程式碼沒有縮排,或者隨機縮排,那麼和給你看一篇火星文論文沒有區別
for (var i = 0; i < 100; i++) {

if (true) {
function fn() {  
for (var j = 0; j < 100; j++) {
}
}
for (var j = 0; j < 100; j++) {
}
}
}
什麼是可維護性的程式碼,這十六點得知道

整整齊齊的就是看不懂

  • 我們嚴格保持了程式碼縮排以後, 雖然程式碼意義不一定看得懂, 但是程式碼結構我能看得懂了
for (var i = 0; i < 100; i++) {

   if (true) {
       function fn() {  
           for (var j = 0; j < 100; j++) {
           }
       }
       for (var j = 0; j < 100; j++) {
       }
   }
}
  • 這個時候就可以嘗試下改一改了

三、註釋

在任何一個語言裡面,都是有註釋的

語言規範裡定義註釋,不是為了讓你學了玩的,就是為了讓你對程式碼進行一些標註的

大型程式碼塊,和大量變數堆積的地方,都要有清楚的註釋,用來表明這個程式碼塊或者說這一堆變數是幹什麼用的,尤其是函式,儘量做到每一個函式的前面都有一個說明註釋。

/*

* fn 獲取範圍之間隨機整數的函式
* @param {Number} a 範圍開始的數字
* @param {Number} b 範圍結束的數字
* @return {Number} 範圍內的隨機整數
*/
function fn(a, b) { ... }
    • 每一個函式都應該有引數說明,是否有返回值,返回值是什麼
    • 因為這些內容在函式定義中是不能直觀看到了,需要閱讀程式碼才可以
    • 當你寫明瞭這些以後,閱讀性就大大提高了
    • 假設,你的函式塊裡面涉及到很複雜的演算法,最好也是在說明註釋裡面標註出來

當你對於一些瀏覽器問題做出的修復,你使用了一些黑科技

    • 那麼你一定要把這些黑科技標註出來,避免別人修改你的程式碼的時候
    • 覺得這些黑科技沒有用,給你刪掉了,導致你修改好的問題又重新出現了

四、變數和函式命名

變數的命名和函式的命名,是最能體現我們自定義的地方

對於每一個變數和函式的命名,我們都儘量準確的給到一個語義,不管你是使用 大駝峰 還是 小駝峰,都要保證看到名字就能知道這個變數或者函式的意義

從變數來說

1、儘量使用名詞,而不是動詞

比如:car / person / show / ...

2、常量來說,要使用大寫字母來表示

比如:TEST / BROWSER / ...

3、區分全域性和私有變數,函式內的私有變數我會以 _ 開頭

比如: _this / ...

從函式來說

1、當函式返回布林值的時候, 一般會以 is 開頭

比如:isEnabled() / isSelected() / ...

2、獲取類的函式一般以 get 開頭

比如:getUserList() / getUserInfo() / ...

3、設定類的一般使用 set 開頭

比如:setName() / setUserInfo() / ...

4、修改類的一般使用 update 開頭

比如:updateName() / updatePrice() / ...

4、程式處理類函式使用 handler 結尾

比如:showEditHandler() / submitHandler() / ...

5、儘可能的透過名字描述清楚函式的作用,不用擔心太長,因為後期打包工具會幫我們處理掉的

比如: getUserInfoById() / delGoodsParamsById() / ...

五、變數型別透明化

因為 JS 是一個弱型別語言,在定義變數的時候,不會限制資料型別

但是我們在給變數賦值的時候,也要儘可能的做到資料型別統一

當你需要定義一些變數,在後期操作中進行賦值的時候

儘可能在定義的時候,給一個初始值表示一下你變數將來要儲存的資料型別

比如:
var count = 0;
var name = '';
var boo = false;
var person = null;
var todoList = [ ];

如果你實在不想給一個初始值

也可以使用註釋的形式表明一下你定義的變數, 將來儲存的是什麼型別的資料

var count /* Number */;
var name /* String */;
var boo /* Boolean */;

六、程式碼書寫習慣

我們要保證一個良好的程式碼書寫習慣

七、鏈式程式設計的習慣

我們來看一下下面這個程式碼

[ ... ].map(function () {

// code ...
}).filter(function () {
// code ...
}).reduce(function () {
// code ...
})

其實沒啥問題, 而且也挺好的
更甚至當程式碼簡單一些的時候有人把它寫成一行

[ ... ].map(function () { ... }).filter(function () { ... }).reduce(function () { ... })

但是到了後期修改的時候,問題就會逐步顯示,一旦修改了第一個,那麼後面的都有可能會出現問題
而且當程式碼量過大的時候,很難保證你不修改序列了

  • 我們可以把上面的程式碼換成下面的方式
[ ... ]

   .map(function () {
   // code ...
   })
   .filter(function () {
   // code ...
   })
   .reduce(function () {
   // code ...
   })

這樣的話,看起來會舒服的多

而且可以利用編輯器的程式碼摺疊,一個函式一個函式的來書寫

八、書寫運算子的習慣

很多人喜歡相對緊湊的書寫結構

比如下面的程式碼

if (year%4===0&&year%100!==0||year%400===0) { ... }

很簡單的一個判斷閏年的程式碼

但是當你的運算子很緊湊的時候,那麼看起來就會比較費眼睛

相對來說,我更喜歡在運算子兩邊都加上空格

讓結構相對鬆散一些,看起來可能也容易一些

我們也不用擔心這些空格,後期處理都會幫我們處理掉的

if ( year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { ... }

還有一種寫法

if ( 

year % 4 === 0 &&
year % 100 !== 0 ||
year % 400 === 0
) { ... }

這個適用於條件比較長的時候使用看起來會更加清晰一些

九、函式呼叫傳遞引數

  • 當呼叫一個函式,需要傳遞一個函式作為引數的時候
  • 我們通常都會直接書寫一個匿名函式或者箭頭函式在引數位置
  • 或者說傳遞一個複雜資料型別作為引數的時候,都會直接講對應的陣列或者物件寫在引數位置
  • 比如下面這段程式碼
$.get('/xxx', {

   a: 100,
   b: 200
}, function (res) {
   // code ...
}, 'json')

程式碼沒有問題,但是一旦物件中資料過多或者函式中程式碼過多的時候後期看起來就會很複雜

我會建議把這些內容單獨書寫出來

var params = {

   a: 100,
   b: 200
}

function success(res) {
   // code ...
}

$.get('/xxx', params, success, 'json')

這樣一來, 不管是修改, 還是增加一些內容, 都會比較方便了

十、功能性函式的單獨封裝

把我們自定義的一些功能性函式進行單獨的封裝,放在一個單獨的 JS 檔案中進行引入或者匯入使用,其實就是模組化的概念

十一、鬆散耦合

對於比較難以閱讀的程式碼來說,強耦合的程式碼是最難閱讀的,JS 程式碼本身層面上的耦合我們就不說了,大家都應該瞭解物件導向程式設計和模組化程式設計

十二、HTML 和 JavaScript 的耦合

在前端開發中,我們經常會見到有些人寫程式碼會把一些簡單的事件直接寫到 html 結構上

<button >

從程式碼層面上來說完全沒有問題但是實際上,這個是 HTML 和 JavaScript 的強耦合現象第一: 每次對於程式碼進行的修改,都要從 HTML 和 JavaScript 兩個位置去進行修改第二: 程式碼引入位置不可變,一定要保證在使用者點選之前就已經有函式存在了,不然一定會報錯的

比較好的方法就是進行 HTML 和 JavaScript 的分離

在 .js 檔案中獲取 DOM 元素
透過事件繫結的形式來完成操作

var btn = document.querySelector('button')

btn.addEventListener('click', doSomething)

還有一種情況更常見, 就是在 JS 程式碼中為了渲染頁面而進行字串拼接

container.innerHTML = `

<div>
...
<p> ... </p>
<span> ... </span>
</div>
`

這個程式碼也是完全沒有問題的,而且大部分同學都會這樣書寫程式碼,因為省時省力但是這樣的情況,一旦渲染到頁面上,出現樣式問題需要調整的時候我們在 HTML 結構中很難找到內容來修改,必須要到 JavaScript 程式碼裡面去修改如果我們的字串拼接是在迴圈裡面完成的話,那麼有可能你新增一個或者刪除一個標籤的時候,導致整個頁面崩潰

比較好的做法

使用一些第三方小指令碼或者模板引擎來進行渲染:

比如:art-template / e.js / ...
真的需要這樣渲染的時候,那麼在原始 html 結構中以註釋的形式留下一部分渲染內容

<div class="container">

   <!-- 商品詳情資訊渲染結構
   <div>
       ...
       <p> ... </p>
       <span> ... </span>
   </div>
   -->
</div>
  • 當 HTML 和 JavaScript 解耦以後
  • 可以大量節省我們的排錯時間, 和錯誤的準確定位

十三、CSS 和 JavaScript 的耦合

在前端的開發中,使用 JS 來操作一些元素的樣式,是在常見不過的事情了

比如我們經常會寫

ele.style.color = 'red' ;

ele.style.display = 'none' ;

這樣書寫程式碼其實沒有大問題
對於渲染也不會造成很大的困擾
但是,一旦我們需要修改樣式的時候,那麼就比較麻煩了
因為有的樣式可能需要在 .css 檔案內修改,有的樣式需要在 .js 檔案內修改

  • 比較好的做法是, 把我們需要修改的樣式寫成一個單獨類名下
  • 放在 .css 檔案內
  • 我們在程式碼裡面透過操作元素的類名來進行修改
ele.classList.add('active') 

ele.classList.remove('active')

這樣做保證了樣式和行為的分離,我們在調整頁面樣式的時候,不需要 JS,直接在 CSS 中修改就可以

十四、事件處理 和 應用邏輯 的耦合

在開發過程中, 我們經常要處理一些事件,並且在事件裡面要進行一些邏輯的運算

比如:我們在點選登入的時候,要對使用者填寫的內容進行一個正則的驗證,然後提交到伺服器

ele.addEventListener('submit', function () {

   let username = xxx.value
   let password = xxx.value
   
   // 正則驗證
   if ( ... ) { ... }
   
   if ( ... ) { ... }
   
   // 提交到伺服器
  var xhr = new XMLHttpRequest()
   xhr.open( ... )
   xhr.send( ... )
   xhr.() { ... }
})

這是一段合法的程式碼
但是函式里麵包含了太多的內容
有事件處理
有邏輯處理
有請求傳送
這樣就相當於在一個函式里面做了太多的事情
這個程式碼的邏輯運算還是比較少的,但是一旦邏輯運算多了以後,那麼後期閱讀的時候就很麻煩了

我們可以把裡面的邏輯運算和請求傳送都單獨提取出來,變成下面這個形式:

function validateValue(val) {

   // 正則驗證
   if ( ... ) { ... }
   
   if ( ... ) { ... }
   
   // 將驗證結果返回
   return true // or false
}

function sendAjax() {
   // 傳送請求的業務邏輯
}

ele.addEventListener('submit', function () {
   let username = xxx.value
   let password = xxx.value
   
   // 正則驗證
   if (!validateValue( xxx )) return
   
   // 提交到伺服器
  sendAjax()
})

這樣一來,只要我們給函式寫好註釋,那麼後期的時候,哪裡出現問題,我們可以快速準確的定位問題所在位置

十五、尊重物件所有權

  • JavaScript 的動態天性決定了沒有什麼是不能修改的
  • 從程式碼層面出發,我們可以修改任何內容,包括向 Object 的 prototype 上擴充套件一些方法,,向 Array 的 prototype 上擴充套件一些方法
  • 但是在真實的企業級開發過程中,我們要絕對的尊重每一個物件的所有權

不要修改任何不屬於你的程式碼,如果某一個物件不是由你負責建立或者維護,那麼你也不要修改他的建構函式

在好久好久以前:

我接觸過一個叫做 prototype 的第三方庫
它裡面向 document 物件上擴充套件了一個叫做 getElementsByClassName 的方法
是不是看起來很無聊,但是在沒有 getElementsByClassName 的年代,確實很好用
並且,擴充套件的這個 getElementsByClassName 方法的返回值是一個 Array 並不是我們後來使用的 NodeList
而且還在例項身上擴充套件了一個叫做 each() 的方法,專門用來遍歷
我們用起來的時候就會很方便

document.getElementsByClassName('item').each() 

這個很好,而且對程式碼開發進行了簡化
但是,一旦瀏覽器廠商開始支援這個方法了,那麼你的方法就會出現問題了
後來,在所有瀏覽器廠商都支援了 getElementsByClassName 以後
當在使用這個方法的時候,因為和原生的重名了
會出現程式碼的大面積報錯

這個就是尊重程式碼所有權

因為你不知道瀏覽器廠商什麼時候會 告知 或 不告知 的更新一些內容,或者修改一些 API

所以, 不要修改任何不屬於你的內容

十六、儘量不宣告全域性變數

和尊重物件所有權有密切關係的就是儘可能少的宣告全域性變數

拋開變數汙染的層面不說,我們的每一個全域性變數其實都是在向 window 上新增成員

var name = 'Jack' 

function getInfo() { ... }

這都是全域性變數,用起來也沒什麼問題
但是也確實是在 window 上掛載了兩個名字

我們在開發自己的程式碼的時候, 儘可能的在全域性製作一個名稱空間,然後把我們所有需要的內容全部放在裡面

var myApp = {

   name: 'jack',
   getInfo () { ... }
}

這樣一來, 我們只是向 window 上掛載了一個 myApp
剩下的所有東西都在我自己的名稱空間裡面
一旦出現問題,你能準確的知道是你自己定義的變數或者方法出現了問題,還是原生的變數或者方法出現了問題

這個也是前端從沒有模組化到模組化開發的演變過程的原始階段:

    • 獨立名稱空間
    • IIFE
    • AMD / CMD
    • CommonJS
    • ES6 模組化

十七、習慣使用常量

我們在開發的過程中,經常要使用一些變數來操作某些內容

    • 任何出現一次以上的內容,都應該提取出來變成一個常量的定義
    • 任何一個需要顯示給使用者看到的文字內容,都應該提取出來變成一個常量
    • 任何一個變數,在定義的時候都要考慮,將來會不會發生變化,如果不發生變化,那麼就直接定義成常量
    • 包括我們在操作一些類名的時候,應該把這些類名提取出來做成常量,然後統一操作

這樣一來,我們可以避免因為不小心修改變數而導致出現的問題,也可以在程式碼的各個部分保證程式碼資料的統一,避免一個東西這裡修改了,那裡沒有修改的問題

什麼是可維護性的程式碼,這十六點得知道


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2709083/,如需轉載,請註明出處,否則將追究法律責任。

相關文章