編寫(xiě)高質(zhì)量可維護(hù)的代碼:一目了然的注釋
前言
有一些人認(rèn)為,好的代碼是自我解釋的。合適的命名和優(yōu)秀的代碼的確可以減輕開(kāi)發(fā)人員閱讀代碼的工作量,對(duì)于不是特別復(fù)雜的代碼可能確實(shí)可以做到自我解釋。但并不是所有場(chǎng)景都可以做到這一點(diǎn),我們一起來(lái)了解一下“注釋”吧。
編程語(yǔ)言中對(duì)“注釋”的解釋
注釋就是對(duì)代碼的解釋和說(shuō)明。注釋是開(kāi)發(fā)人員在編寫(xiě)程序時(shí),給一段代碼的解釋或提示,有助于提高程序代碼的可讀性。注釋不會(huì)被計(jì)算機(jī)編譯。
要不要加注釋?為什么要加注釋?
注釋的存在就是為了方便自己的二次閱讀和代碼維護(hù)以及項(xiàng)目交接??梢愿玫睦斫獯a,有助于提高協(xié)作效率,加快開(kāi)發(fā)進(jìn)程。
試想,你添加了一段邏輯較為復(fù)雜的代碼,幾個(gè)月后再看,還能不能迅速看懂?你剛剛接手一個(gè)老項(xiàng)目,項(xiàng)目里基本沒(méi)有注釋且邏輯復(fù)雜,你能高效率的看懂代碼和了解業(yè)務(wù)嗎?
所以添加注釋還是有一定必要滴。
基礎(chǔ)篇
快捷鍵 windows 系統(tǒng): Ctrl+/ ios 系統(tǒng): Command+/
注釋的分類
- HTML中的注釋
 
<div>
  這是一行文字
  <!-- 這是一行被注釋的文字 -->
</div>
- 
    
CSS 中的注釋
 
- <style>
 - div {
 - /* color: #fff; */
 - }
 - </style>
 
- div {
 - /* color: #fff; */
 - }
 
- div {
 - /* color: #fff;*/ /* 多行注釋*/
 - // font-size: 14px; // 單行注釋
 - background: #000;
 - }
 
- 在 HTML 中
 - 在 .css文件中
 - 在 .less 或 .scss 文件中
 - 
    
JS 中的注釋
- 
        
用法
- 可用于解釋 JavaScript 代碼,增強(qiáng)其可讀性。
 - 也可以用于阻止代碼執(zhí)行。
 
 - 
        
單行注釋(行注釋)—— 以
//開(kāi)頭。任何位于//之后的文本都會(huì)被注釋 
 - 
        
 
- 
    
多行注釋(塊注釋)——以
/*開(kāi)頭,以*/結(jié)尾。任何位于/*和*/之間的文本都會(huì)被注釋 
- /*
 - 這是多行注釋
 - 定義一個(gè)數(shù)組
 - */
 - var ary = [];
 
- * 用注釋來(lái)阻止代碼執(zhí)行 —— 被注釋的 JS 代碼將不被執(zhí)行
 - ```js
 - //alert("123") // 執(zhí)行時(shí)未彈出該信息
 - alert("456") // 執(zhí)行時(shí)彈出該信息
 - ```
 
- 
    
函數(shù)注釋
- 一般以 
/**開(kāi)頭,以*/結(jié)尾。任何位于/**和*/之間的文本都會(huì)被注釋 
 - 一般以 
 
- /**
 - * 提交
 - *
 - * @method onSubmit
 - * @param {[Object]} 提交數(shù)據(jù)
 - * @return {[Bollean]} [返回是否提交成功 ]
 - */
 - const onSubmit = (params = {}) => {
 - const result = false;
 - if (params) {
 - result = true;
 - }
 - return result;
 - };
 
- 特殊標(biāo)記注釋
    
- TODO 在該注釋處有功能代碼待編寫(xiě),待實(shí)現(xiàn)的功能在說(shuō)明中會(huì)簡(jiǎn)略說(shuō)明
 - FIXME 在該注釋處代碼需要修正,甚至代碼是錯(cuò)誤的,不能工作,需要修復(fù),如何修正會(huì)在說(shuō)明中簡(jiǎn)略說(shuō)明
 - XXX 在該注釋處代碼雖然實(shí)現(xiàn)了功能,但是實(shí)現(xiàn)的方法有待商榷,希望將來(lái)能改進(jìn),要改進(jìn)的地方會(huì)在說(shuō)明中簡(jiǎn)略說(shuō)明
 - NOTE 在該注釋處說(shuō)明代碼如何工作
 - HACK 在該注釋處編寫(xiě)得不好或格式錯(cuò)誤,需要根據(jù)自己的需求去調(diào)整程序代碼
 - BUG 在該注釋處有 Bug
 
 
- // TODO功能未完成,待完善
 - // FIXME 待修復(fù)
 - // XXX 實(shí)現(xiàn)方法待確認(rèn)
 - // NOTE 代碼功能說(shuō)明
 - // HACK 此處寫(xiě)法有待優(yōu)化
 - // BUG 此處有Bug
 - const arr = []
 
Tips:
- 為什么 
//注釋可以在 .less 或 .scss 文件中使用,但是在 .html 和 .css 文件中不生效?- 在 MDN 中關(guān)于 CSS 注釋只有 
/* */一種語(yǔ)法。但是在 LESS 和 SCSS 中支持注釋的語(yǔ)法和 js 中保持一致,有單行注釋//和多行注釋/* */兩種。單行注釋編譯之后不會(huì)被保留。 
 - 在 MDN 中關(guān)于 CSS 注釋只有 
 - 單行注釋為什么有時(shí)候?qū)懺诖a上方,有時(shí)候?qū)懺诖a后方?
    
- 注釋可以書(shū)寫(xiě)在代碼中的任意位置。個(gè)人理解,一般寫(xiě)在代碼上方的時(shí)候意為對(duì)后面一段代碼的注釋,而寫(xiě)在代碼后方的時(shí)候意為對(duì)本行代碼的注釋。
 
 
注釋寫(xiě)法規(guī)范
- 
    
文件注釋
- 位于文件頭部,一般包含概要、作者、版本改動(dòng)信息以及修改時(shí)間等內(nèi)容
 
 
- /*
 - * 簡(jiǎn)述當(dāng)前文件功能
 - * @author 作者名稱
 - * @version 版本號(hào) 最近編輯時(shí)間
 - * @description 該版本改動(dòng)信息
 - */
 
- 
    
單行注釋
- 總是在 
//后留一個(gè)空格 
 - 總是在 
 
- // 這是一行注釋
 
- 
    
多行注釋
- 總是保持星號(hào)縱向?qū)R(結(jié)束符前留一個(gè)空格)
 - 不要在開(kāi)始符、結(jié)束符所在行寫(xiě)注釋
 - 盡量使用單行注釋代替多行注釋
 - 注釋函數(shù)時(shí),推薦使用多行注釋
 
 
- /*
 - 這里有一行注釋
 - 這里有一行注釋
 - 這里有一行注釋
 - */
 
- 
    
函數(shù)注釋
- 其間每一行都以 
*開(kāi)頭,且與第一行第一個(gè)*對(duì)齊 - 注釋內(nèi)容與 
*間留一個(gè)空格 - 必須包含標(biāo)簽注釋。例: ```javascript /**
 
 - 其間每一行都以 
 - 
    
方法說(shuō)明
 - 
    
@method 方法名
 - 
    
@for 所屬類名
 - 
    
@param {參數(shù)類型} 參數(shù)名 參數(shù)說(shuō)明
 - 
    
@return {返回值類型} 返回值說(shuō)明
 - 
    
/
 
注釋常用標(biāo)簽用法
- @type {typeName}
    
*表示任何類型?表示可以為null!表示不能為null[]表示數(shù)組 ```javascript /**
 - @type {number}
 - / var foo1;
 
/**
- @type {*}
 - @desc 任何類型
 - / var foo2;
 
/**
- @type {?string}
 - @desc string或者null
 - / var foo3;
 
- * @param {<type>} name - some description
 - * 非必傳參數(shù)需給參數(shù)名加上'[]'
 - * 參數(shù)如有默認(rèn)值需用'='表示
 - * 如果參數(shù)是object,可繼續(xù)用`@param`對(duì)其屬性進(jìn)行詳細(xì)說(shuō)明
 - * 若干個(gè)參數(shù)用`...`表示
 - ```javascript
 - /**
 - * @func
 - * @desc 一個(gè)帶參數(shù)的函數(shù)
 - * @param {string} a - 參數(shù)a
 - * @param {number} b=1 - 參數(shù)b默認(rèn)值為1
 - * @param {string} c=1 - 參數(shù)c有兩種支持的取值 1—表示x 2—表示xx
 - * @param {object} d - 參數(shù)d為一個(gè)對(duì)象
 - * @param {string} d.e - 參數(shù)d的e屬性
 - * @param {object[]} g - 參數(shù)g為一個(gè)對(duì)象數(shù)組
 - * @param {string} g.h - 參數(shù)g數(shù)組中一項(xiàng)的h屬性
 - * @param {string} [j] - 參數(shù)j是一個(gè)可選參數(shù)
 - */
 - function foo(a, b, c, d, g, j) {}
 - /**
 - * @func
 - * @desc 一個(gè)帶若干參數(shù)的函數(shù)
 - * @param {...string} a - 參數(shù)a
 - */
 - function bar(a) {}
 
了解更多可查看 JSDoc
拓展篇
IE 條件注釋(IE5+)
IE 條件注釋分為以下幾種情況:
- 
    
只允許 IE 解釋執(zhí)行
<!--[if IE]><![endif]--> - 
    
只允許 IE 特定版本解釋執(zhí)行
<!--[if IE 7]><![endif]--> - 
    
只允許非 IE 特定版本執(zhí)行注釋
<!--[if !IE 7]><![endif]--> - 
    
只允許高于或低于 IE 特定版本執(zhí)行注釋
<!--[if gt IE 7]><![endif]--> 
- <head>
 - <title>IE 條件注釋</title>
 - <!-- 是 IE 時(shí) -->
 - <!--[if IE]>
 - <link href="style.css" rel="stylesheet" type="text/css" />
 - <![endif]-->
 - <!-- 是 IE 7 時(shí) -->
 - <!--[if IE 7]>
 - <link href="style.css" rel="stylesheet" type="text/css" />
 - <![endif]-->
 - <!-- 不是 IE 7 時(shí) -->
 - <!--[if !IE 7]>
 - <link href="style.css" rel="stylesheet" type="text/css" />
 - <![endif]-->
 - <!-- 大于 IE 7 時(shí) -->
 - <!--[if gt IE 7]>
 - <link href="style.css" rel="stylesheet" type="text/css" />
 - <![endif]-->
 - <!-- 小于 IE 7 時(shí) -->
 - <!--[if lt IE 7]>
 - <link href="style.css" rel="stylesheet" type="text/css" />
 - <![endif]-->
 - </head>
 
# (井號(hào))注釋 和 ''' (三引號(hào))注釋
#一般出現(xiàn)在各種腳本配置文件中,用法與 JS 單行注釋//基本相同。Python 中也常常用到'''是 Python 中的多行注釋語(yǔ)法,用''''''包含被注釋的段落
- # python 的單行注釋一
 - print("I could have code like this.") # python 的單行注釋二
 
print("This won't run.") # 被注釋的代碼
''' 被三引號(hào)包裹的段落 可以隨意折行 也可以注釋代碼 print("This won't run.") '''
- #### 注釋“被執(zhí)行”了?
 - 眾所周知,注釋的代碼是不會(huì)被執(zhí)行的。但是小編在查資料時(shí)看到了一段比較有意思的代碼, Java 中的一行注釋“被執(zhí)行”了?
 - ```java
 - public class Test {
 - public static void main(String[] args) {
 - String name = "趙大";
 - // \u000dname="錢二";
 - System.out.println(name);
 - }
 - }
 
這段代碼執(zhí)行后的結(jié)果為 錢二 ,也就是說(shuō)在這段代碼中,“被注釋”的那行代碼生效了!
這段代碼的問(wèn)題出在 \u000d 這串特殊字符上。 \u000d 是一串 Unicode 字符,代表?yè)Q行符。Java 編譯器不僅會(huì)編譯代碼,還會(huì)解析 Unicode 字符。在上面這段代碼把 \u000d 給解析了,后面的代碼就到了下面一行,超出了被注釋的范圍(單行注釋的注釋范圍僅在當(dāng)前行),所以執(zhí)行結(jié)果為 錢二 而非 趙大 。(如下)
- public class Test {
 - public static void main(String[] args) {
 - String name = "趙大";
 - //
 - name="錢二";
 - System.out.println(name);
 - }
 - }
 
 所以本質(zhì)上在代碼執(zhí)行的時(shí)候 name="錢二" 并沒(méi)有被注釋,而是被換了行(奇怪的知識(shí)增加了)。 所以切記,注釋確實(shí)是不會(huì)被執(zhí)行的哦!
注釋相關(guān)插件
 在這里推薦幾個(gè)個(gè)人認(rèn)為比較好用的注釋相關(guān)的Vscode插件,可在 setting.json 文件下自定義設(shè)置(可通過(guò) '文件—首選項(xiàng)—設(shè)置',打開(kāi) Vscode 文件 settings.json )
- koroFileHeader 在 vscode 中用于生成文件頭部注釋和函數(shù)注釋的插件
 - 文件頭部添加注釋
    
- 在文件開(kāi)頭添加注釋,記錄文件信息/文件的傳參/出參等
 - 支持用戶高度自定義注釋選項(xiàng), 適配各種需求和注釋。
 - 保存文件的時(shí)候,自動(dòng)更新最后的編輯時(shí)間和編輯人
 - 快捷鍵: 
window:ctrl+alt+i,mac:ctrl+cmd+i,linux:ctrl+meta+i 
 

- 在光標(biāo)處添加函數(shù)注釋
    
- 在光標(biāo)處自動(dòng)生成一個(gè)注釋模板
 - 支持用戶高度自定義注釋選項(xiàng)
 - 快捷鍵: 
window:ctrl+alt+t,mac:ctrl+cmd+t,linux:ctrl+meta+t - 快捷鍵不可用很可能是被占用了
 - 可自定義默認(rèn)參數(shù)
 
 

- Better Comments 通過(guò)使用警報(bào),信息,TODO 等進(jìn)行注釋來(lái)改善代碼注釋。使用此擴(kuò)展,您將能夠?qū)⒆⑨尫诸悶椋?
    
- 快訊
 - 查詢
 - 待辦事項(xiàng)
 - 強(qiáng)調(diào)
 - 注釋掉的代碼也可以設(shè)置樣式,以使代碼不應(yīng)該存在
 - 可自定義指定其他所需的注釋樣式
 
 

- TODO Highlight 突出顯示TODO,F(xiàn)IXME和任何關(guān)鍵字
    
- 高亮內(nèi)置關(guān)鍵字,可通過(guò)自定義設(shè)置覆蓋外觀
 - 也可自定義關(guān)鍵字
 
 

用事實(shí)說(shuō)話
口說(shuō)無(wú)憑,眼見(jiàn)為實(shí)。下面我們看下實(shí)際開(kāi)發(fā)中的具體情況:
- 沒(méi)有注釋
 
- const noWarehousetemIds = beSelectSkucontainer.reduce((arr, itemId) => {
 - const res = Object.keys(selectRowskey[itemId]).every((skuId) => {
 - const sku = selectRowskey[itemId][skuId];
 - return !!sku.warehouseCode || lodashGet(warehouses, '[0].code');
 - });
 - if (!res) {
 - arr.push(itemId);
 - }
 - return arr;
 - }, []);
 - if (noWarehousetemIds.length > 0 || noStockItemIds.length > 0) {
 - const itemIds = Array.from(new Set([...noWarehousetemIds, ...noStockItemIds]));
 - const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);
 - return Modal.warning({
 - title: '錯(cuò)誤提示',
 - content: `“${itemNames.join(',')}”庫(kù)存信息未完善,請(qǐng)完善庫(kù)存信息`,
 - });
 - }
 
- 一般般的注釋
 
- // 遍歷當(dāng)前所有選中的sku,查找出沒(méi)有庫(kù)存的itemId
 - const noStockItemIds = beSelectSkucontainer.reduce((arr, itemId) => {
 - const res = Object.keys(selectRowskey[itemId]).every((skuId) => {
 - const sku = selectRowskey[itemId][skuId];
 - return !!sku.stockQuantity;
 - });
 - if (!res) {
 - arr.push(itemId);
 - }
 - return arr;
 - }, []);
 - // 有一條sku的庫(kù)存為空時(shí)進(jìn)入校驗(yàn)
 - if (noStockItemIds.length > 0) {
 - const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);
 - return Modal.warning({
 - title: '錯(cuò)誤提示',
 - content: `“${itemNames.join(',')}”庫(kù)存信息未完善,請(qǐng)完善庫(kù)存信息`,
 - });
 - }
 
- 更好的注釋
 
- // 遍歷當(dāng)前所有選中的sku,查找出沒(méi)有庫(kù)存的itemId
 - const noStockItemIds = beSelectSkucontainer.reduce((arr, itemId) => {
 - // selectRowskey是一個(gè)對(duì)象,以itemId為key,sku對(duì)象作為value,sku對(duì)象以skuId作為key,sku作為value,只有selectRowskey下所有itemId下的sku都有庫(kù)存才算校驗(yàn)通過(guò)
 - /*
 - 數(shù)據(jù)格式:
 - selectRowskey: {
 - 12345678: { // itemId
 - 123456: { // skuId
 - name: 'sku',
 - }
 - }
 - }
 - */
 - const res = Object.keys(selectRowskey[itemId]).every((skuId) => {
 - const sku = selectRowskey[itemId][skuId];
 - return !!sku.stockQuantity;
 - });
 - // 只要有一條sku沒(méi)有庫(kù)存時(shí),就塞到arr中,返回給noStockItemIds數(shù)組
 - if (!res) {
 - arr.push(itemId);
 - }
 - return arr;
 - }, []);
 - // 有一條sku的庫(kù)存為空時(shí)進(jìn)入校驗(yàn)
 - if (noStockItemIds.length > 0) {
 - // 根據(jù)id查找商品名稱
 - const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);
 - Modal.warning({
 - title: '錯(cuò)誤提示',
 - content: `“${itemNames.join(',')}”庫(kù)存信息未完善,請(qǐng)完善庫(kù)存信息`,
 - });
 - }
 
看到上面這段代碼可以很明顯的體會(huì)到有沒(méi)有注釋以及注釋寫(xiě)的清不清楚的重要性。若是寫(xiě)了注釋但仍然看不懂,那還不如不寫(xiě)。
所以注釋也不是隨便寫(xiě)一寫(xiě)就可以的,要描述某段代碼的功能,注明邏輯,讓開(kāi)發(fā)者可以”無(wú)腦“瀏覽。
之前在工作群中看到有人發(fā)過(guò)這樣一張圖(如下圖),個(gè)人認(rèn)為是一個(gè)很好的代碼注釋的范例:

結(jié)語(yǔ)
看到這里,對(duì)于注釋的重要性各位已經(jīng)有自己的認(rèn)知。還有幾點(diǎn)是我們寫(xiě)注釋時(shí)需要注意的:
- 
    
注釋內(nèi)容要簡(jiǎn)潔、清楚明了。注釋簡(jiǎn)述功能或?qū)崿F(xiàn)邏輯即可,無(wú)需每行代碼都添加注釋
 - 
    
代碼若有修改,切記同步修改對(duì)應(yīng)的注釋。不要出現(xiàn)過(guò)期的注釋,否則會(huì)起到反作用
 















 
 
 






 
 
 
 