當前位置:編程學習大全網 - 源碼下載 - 如何在JavaScript中謹慎使用代碼註釋

如何在JavaScript中謹慎使用代碼註釋

前言

最令程序員頭痛的事情莫過於閱讀別人的代碼,但其實時間壹久閱讀自己的代碼也會很痛苦。 問題不是出在『自己或別人』,而是在代碼本身。

必要的註釋可以闡明實現細節和設計意圖,以此節約自己和別人的時間。 然而很多時候註釋起的作用卻適得其反,比如自動生成的過多的註釋分散閱讀者的註意力, 而過期的失效的註釋更是誤導閱讀者。

自動生成的註釋

代碼註釋的泛濫想必是從Eclipse,Visual Studio等IDE開始的。 這些IDE提供了很多快捷功能,生成類/接口的骨架,具有Getter/Setter的屬性等等。 如果用過IDE,下面的代碼妳壹定不會陌生:

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

}

上述6行代碼中的4行註釋包含的信息量是0,既沒有闡釋參數args是何物,也沒有說明main的用途。 然而大量的項目中都充斥著這樣的自動生成註釋。

『建議』:如果有參數或機制需要說明,請補充這些信息。否則請刪除自動生成註釋。 當然,用於生成文檔的註釋除外。

過多的註釋

總會有人不厭其煩地編寫長篇累牘的註釋,或無微不至,或語焉不詳,或晦澀難懂,或文采飛揚。 總之沒有幫助我更快閱讀代碼的註釋都是失敗的註釋。

為了說明問題,Harttle克隆了4.x Linux Kernel源碼, 來大致分析壹下其註釋行數。 我們知道內核代碼95%以上是C語言,所以統計.c文件就足夠說明問題了。

linux git:(master) git clone git@github.com:torvalds/linux.git --depth=1

linux git:(master) find . -name "*.c" -o -name "*.h" -exec grep -E '^\s*((\*)|(/[/*]))' {} \; | wc -l

724804

linux git:(master) find . -name "*.c" -o -name "*.h" -exec cat {} \; | wc -l

4018961

linux git:(master) node

> 724804/(4018961-724804)

0.22002715717556875

內核倉庫中的代碼大概是402萬行(未移除空行),其中註釋72萬行,占比22%。 Linux內核使用低級的C語言編寫,涉及到復雜的CPU調度、內存管理,驅動程序。 因此註釋會偏多壹些,壹般的項目註釋應小於這個數值。

『建議』:如果妳的代碼中註釋超過了20%,那麽顯然妳過度註釋了。

文件頭註釋

很多編輯器/IDE都會生成默認的文件頭,例如:

/**

* @file /tmp/xxx.js

* @author harttle(yangjvn@126.com)

* @date 2016-08-30 22:33

* @description A XXX Implementation for XXX.

*/

文件頭註釋清晰地列出了文件的作者、功能描述等信息,看起來很有用。 不過這樣的文件頭存在的問題在於其維護性:

其他人做小的修改時未必會修改@author,甚至連@author都不知道現在該文件已經面目全非。

每次移動該文件,是否還需要花功夫更新 @file 信息?

誰會在每次代碼修改後記得更新 @description,於是@description也總是誤導讀者

文件頭註釋意在維護代碼文件的元信息,以便在分發和部署過程中維護作者版權等信息。 然而在擁有版本控制的代碼倉庫中,這些信息不再需要手動維護,甚至可以通過git blame查看每壹行代碼的作者和時間信息。

『建議』:使用版本控制工具,刪除文件頭註釋。版權信息可在構建或分發時生成。

冗余的註釋

意圖非常清楚的代碼原則上不需要註釋,多余的註釋反而會造成維護性問題。 尤其是非英語母語的作者常常會掉到這個坑裏。比如變量和函數的註釋:

/*

* 獲取用戶數目

*/

function getUserCount(){

// 用戶的列表

var userList = [];

}

這不是廢話麽!冗余的註釋問題仍然在於維護性,例如調整函數功能、調整參數順序, 或者更換變量名時我們不得不更新這些註釋。否則這些註釋就會誤導下壹個讀者。

建議:不說廢話。

抽取註釋到標識符

可能讀者也會有這樣的經驗:當我們寫了壹大段代碼時,往往需要把它們分為幾塊。 然後在每壹塊開頭添加壹段註釋。例如:

function calcTotalCharge(movies, user){

// Calculate Movie Charge

var movieCharge = 0;

for(var i=0; i<movies.length; i++){

var charge = 0;

if(movie.type === 'discount'){

charge = movie.charge * 0.8;

}

else if(movie.type === 'short'){

charge = movie.charge * 2;

}

else if(movie.type === 'normal'){

charge = movie.charge;

}

movieCharge += charge;

}

// Calculate User Charge

var rentCharge = 0;

if(user.isVIP1){

rentCharge = 10;

}

if(user.isVIP2){

rentCharge = 200;

}

else if(user.isVIP3){

rentCharge = 300;

}

else if(user.isVIP4){

rentCharge = 500;

}

// Calculate Total Charge

return movieCharge + rentCharge;

}

上述代碼中的三段註釋確實加速了閱讀代碼的速度, 但每當代碼需要註釋才能讀懂時就應該警醒:是不是結構設計有問題。 對於上述代碼,我們可以通過更加可復用的結構來消除註釋:

function calcTotalCharge(movies, user){

return calcMovieCharge(movies) + calcUserCharge(user);

}

function calcMovieCharge(movies){

var total = 0;

for(var i=0; i<movies.length; i++){

total += calcSingleMovieCharge(movie);

}

return total;

}

function calcSingleMovieCharge(movie){

if(movie.type === 'discount') return movie.charge * 0.8;

else if(movie.type === 'short') return movie.charge * 2;

else if(movie.type === 'normal') return movie.charge;

return 0;

}

function calcUserCharge(user){

if(user.isVIP1) return 10;

else if(user.isVIP2) return 200;

else if(user.isVIP3) return 300;

else if(user.isVIP4) return 500;

return 0;

}

代碼重構之後原來的註釋就變得毫無意義,代碼意圖都被清晰的表述在標識符的命名中。 通常重構會帶來代碼量的減小,因為封裝了分支、每個單元的邏輯也更加明確。

建議:當我們發現不得不進行註釋時,需要警醒是否結構設計發生了問題。

有用的註釋

至此Harttle已描述了這麽多反模式,並非為了說明代碼註釋不重要。 而是為了說明『代碼註釋存在的意義在於幫助理解代碼本身』。 例如在編寫壹些Trick,Polyfill,臨時代碼,以及復雜算法時,註釋變得相當重要。 例如:

Tricks and Polyfills。有時簡單的Trick就可解決多數問題問題, 為沒必要編寫復雜的普適算法, 例如檢測瀏覽器的DOM API支持,檢測AMD/CommonJS環境等等。 這時我們需要清晰地說明這些Trick的意圖,甚至可以將這些代碼抽離為polyfill模塊。

復雜算法。有時我們會編寫數學性非常強的算法,壹眼望去不知所雲。 在開始這些算法前清晰地說明其意圖何在,讀者也就不必花大功夫讀懂這些數學了。

公有接口。模塊的對外接口從邏輯上定義了模塊類型,公有接口代碼也更容易被人讀到。 尤其是JavaScript接口:如果不註釋options中到底是什麽,誰曉得接口如何使用。

  • 上一篇:DNF裏面所謂的《BUff》是什麽意思,用中文給我翻譯出來!
  • 下一篇:數獨求解算法
  • copyright 2024編程學習大全網