編寫高質量可維護的代碼:一目瞭然的註釋

這是第 71 篇不摻水的原創,想獲取更多原創好文,請搜索公衆號關注咱們吧~ 本文首發於政採雲前端博客:編寫高質量可維護的代碼:一目瞭然的註釋javascript

前言

有一些人認爲,好的代碼是自我解釋的。合適的命名和優秀的代碼的確能夠減輕開發人員閱讀代碼的工做量,對於不是特別複雜的代碼可能確實能夠作到自我解釋。但並非全部場景均可以作到這一點,咱們一塊兒來了解一下「註釋」吧。css

編程語言中對「註釋」的解釋

註釋就是對代碼的解釋和說明。註釋是開發人員在編寫程序時,給一段代碼的解釋或提示,有助於提升程序代碼的可讀性。註釋不會被計算機編譯。html

要不要加註釋?爲何要加註釋?

註釋的存在就是爲了方便本身的二次閱讀和代碼維護以及項目交接。能夠更好的理解代碼,有助於提升協做效率,加快開發進程。前端

試想,你添加了一段邏輯較爲複雜的代碼,幾個月後再看,還能不能迅速看懂?你剛剛接手一個老項目,項目裏基本沒有註釋且邏輯複雜,你能高效率的看懂代碼和了解業務嗎?java

因此添加註釋仍是有必定必要滴。python

基礎篇

快捷鍵 windows: ctrl+/ mac: command+/linux

註釋的分類

1、 HTML 中的註釋git

<div>
      這是一行文字
      <!-- 這是一行被註釋的文字 -->
</div>
複製代碼

2、CSS 中的註釋github

  • 在 .html 文件中
<style> div { /* color: #fff; */ } </style>
複製代碼
  • 在 .css 文件中
div {
	/* color: #fff; */
}
複製代碼
  • 在 .less 或 .scss 文件中
div {
	/* color: #fff;*/  /* 多行註釋*/
	// font-size: 14px; // 單行註釋
	background: #000;
}
複製代碼

3、JS 中的註釋編程

  • 用法
    • 可用於解釋 JavaScript 代碼,加強其可讀性。
    • 也能夠用於阻止代碼執行。
  • 單行註釋(行註釋)—— 以 // 開頭。任何位於 // 以後的文本都會被註釋
// 定義一個空數組
var ary = [];
var ary2 = []; // 又定義一個空數組
複製代碼
  • 多行註釋(塊註釋)——以 /* 開頭,以 */ 結尾。任何位於 /**/ 之間的文本都會被註釋
/* 這是多行註釋 定義一個數組 */
var ary = [];
複製代碼
  • 用註釋來阻止代碼執行 —— 被註釋的 JS 代碼將不被執行
//alert("123") // 執行時未彈出該信息
alert("456")  // 執行時彈出該信息
複製代碼
  • 函數註釋
    • 通常以 /** 開頭,以 */ 結尾。任何位於 /***/ 之間的文本都會被註釋
/** * 提交 * * @method onSubmit * @param {[Object]} 提交數據 * @return {[Bollean]} [返回是否提交成功 ] */
const onSubmit = (params = {}) => {
  const result = false;
    if (params) {
			result = true;
		}
	return result;
};
複製代碼

4、特殊標記註釋

  • **TODO **在該註釋處有功能代碼待編寫,待實現的功能在說明中會簡略說明
  • FIXME 在該註釋處代碼須要修正,甚至代碼是錯誤的,不能工做,須要修復,如何修正會在說明中簡略說明
  • **XXX **在該註釋處代碼雖然實現了功能,可是實現的方法有待商榷,但願未來能改進,要改進的地方會在說明中簡略說明
  • **NOTE **在該註釋處說明代碼如何工做
  • **HACK **在該註釋處編寫得很差或格式錯誤,須要根據本身的需求去調整程序代碼
  • **BUG **在該註釋處有 Bug
// TODO功能未完成,待完善
// FIXME 待修復
// XXX 實現方法待確認
// NOTE 代碼功能說明
// HACK 此處寫法有待優化
// BUG 此處有 Bug
const arr = []
複製代碼

Tips:

  • 爲何 // 註釋能夠在 .less 或 .scss 文件中使用,可是在 .html 和 .css 文件中不生效?
    • MDN 中關於 CSS 註釋只有 /* */ 一種語法。可是在 LESS 和 SCSS 中支持註釋的語法和 JS 中保持一致,有單行註釋 // 和多行註釋 /* */ 兩種。單行註釋編譯以後不會被保留。
  • 單行註釋爲何有時候寫在代碼上方,有時候寫在代碼後方?
    • 註釋能夠書寫在代碼中的任意位置。我的理解,通常寫在代碼上方的時候意爲對後面一段代碼的註釋,而寫在代碼後方的時候意爲對本行代碼的註釋。

註釋寫法規範

  • 文件註釋
    • 位於文件頭部,通常包含概要、做者、版本改動信息以及修改時間等內容
/* * 簡述當前文件功能 * @author 做者名稱 * @version 版本號 最近編輯時間 * @description 該版本改動信息 */
複製代碼
  • 單行註釋
    • 老是在 // 後留一個空格
// 這是一行註釋
複製代碼
  • 多行註釋
    • 老是保持星號縱向對齊(結束符前留一個空格)
    • 不要在開始符、結束符所在行寫註釋
    • 儘可能使用單行註釋代替多行註釋
    • 註釋函數時,推薦使用多行註釋
/* 這裏有一行註釋 這裏有一行註釋 這裏有一行註釋 */
複製代碼
  • 函數註釋
    • 其間每一行都以 * 開頭,且與第一行第一個 * 對齊
    • 註釋內容與 * 間留一個空格
    • 必須包含標籤註釋。例:
/** * 方法說明 * @method 方法名 * @for 所屬類名 * @param {參數類型} 參數名 參數說明 * @return {返回值類型} 返回值說明 */
複製代碼

註釋經常使用標籤用法

  • @type {typeName}
    • * 表示任何類型
    • ? 表示能夠爲 null
    • ! 表示不能爲 null
    • [] 表示數組
/** * @type {number} */
var foo1;

/** * @type {*} * @desc 任何類型 */
var foo2;

/** * @type {?string} * @desc string或者null */
var foo3;

複製代碼
  • @param {} name - some description
    • 非必傳參數需給參數名加上 []
    • 參數若有默認值需用 = 表示
    • 若是參數是 Object,可繼續用 @param 對其屬性進行詳細說明
    • 若干個參數用 ... 表示
/** * @func * @desc 一個帶參數的函數 * @param {string} a - 參數a * @param {number} b=1 - 參數b默認值爲1 * @param {string} c=1 - 參數c有兩種支持的取值 1—表示x 2—表示xx * @param {object} d - 參數d爲一個對象 * @param {string} d.e - 參數d的e屬性 * @param {object[]} g - 參數g爲一個對象數組 * @param {string} g.h - 參數g數組中一項的h屬性 * @param {string} [j] - 參數j是一個可選參數 */
 function foo(a, b, c, d, g, j) {}

/** * @func * @desc 一個帶若干參數的函數 * @param {...string} a - 參數a */
function bar(a) {}
複製代碼

瞭解更多可查看 JSDoc

拓展篇

IE 條件註釋(IE5+)

IE 條件註釋分爲如下幾種狀況:

  • 只容許 IE 解釋執行 <!--[if IE]><![endif]-->
  • 只容許 IE 特定版本解釋執行 <!--[if IE 7]><![endif]-->
  • 只容許非 IE 特定版本執行註釋 <!--[if !IE 7]><![endif]-->
  • 只容許高於或低於 IE 特定版本執行註釋 <!--[if gt IE 7]><![endif]-->
<head>
  	<title>IE 條件註釋</title>
  
  	<!-- 是 IE 時 -->
    <!--[if IE]>          <link href="style.css" rel="stylesheet" type="text/css" />     <![endif]-->
  
    <!-- 是 IE 7 時 -->
 	 	<!--[if IE 7]>        <link href="style.css" rel="stylesheet" type="text/css" />     <![endif]-->
 
    <!-- 不是 IE 7 時 -->
  	<!--[if !IE 7]>         <link href="style.css" rel="stylesheet" type="text/css" />     <![endif]-->
  
  	<!-- 大於 IE 7 時 -->
  	<!--[if gt IE 7]>        <link href="style.css" rel="stylesheet" type="text/css" />     <![endif]-->
 
  	<!-- 小於 IE 7 時 -->
   	<!--[if lt IE 7]>        <link href="style.css" rel="stylesheet" type="text/css" />     <![endif]-->
</head>
複製代碼

# (井號)註釋 和 ''' (三引號)註釋

  • # 通常出如今各類腳本配置文件中,用法與 JS 單行註釋 // 基本相同。Python 中也經常用到
  • ''' 是 Python 中的多行註釋語法,用兩個 ''' 包含被註釋的段落
# python 的單行註釋一
	print("I could have code like this.") # python 的單行註釋二

# print("This won't run.") # 被註釋的代碼

''' 被三引號包裹的段落 能夠隨意折行 也能夠註釋代碼 print("This won't run.") '''
複製代碼

註釋 「被執行」 了?

衆所周知,註釋的代碼是不會被執行的。可是小編在查資料時看到了一段比較有意思的代碼, Java 中的一行註釋「被執行」了?

public class Test {
	public static void main(String[] args) {
		String name = "趙大";
		// \u000dname="錢二";
		System.out.println(name);
	}
}
複製代碼

這段代碼執行後的結果爲錢二,也就是說在這段代碼中,「被註釋」的那行代碼生效了!

這段代碼的問題出在 \u000d 這串特殊字符上。\u000d 是一串 Unicode 字符,表明換行符。Java 編譯器不只會編譯代碼,還會解析 Unicode 字符。在上面這段代碼把 \u000d 給解析了,後面的代碼就到了下面一行,超出了被註釋的範圍(單行註釋的註釋範圍僅在當前行),因此執行結果爲 錢二 而非 趙大。(以下)

public class Test {
	public static void main(String[] args) {
		String name = "趙大";
		//
		name="錢二";
		System.out.println(name);
	}
}
複製代碼

因此本質上在代碼執行的時候 name="錢二" 並無被註釋,而是被換了行(奇怪的知識增長了)。 因此切記,註釋確實是不會被執行的哦!

註釋相關插件

在這裏推薦幾個我的認爲比較好用的註釋相關的 Vscode 插件,可在 setting.json 文件下自定義設置(可經過 '文件—首選項—設置',打開 Vscode 文件 settings.json

  • koroFileHeader 在vscode中用於生成文件頭部註釋和函數註釋的插件
  • 文件頭部添加註釋
    • 在文件開頭添加註釋,記錄文件信息/文件的傳參/出參等
    • 支持用戶高度自定義註釋選項, 適配各類需求和註釋。
    • 保存文件的時候,自動更新最後的編輯時間和編輯人
    • 快捷鍵:windowctrl+alt+imacctrl+cmd+ilinuxctrl+meta+i

  • 在光標處添加函數註釋
    • 在光標處自動生成一個註釋模板
    • 支持用戶高度自定義註釋選項
    • 快捷鍵:windowctrl+alt+tmacctrl+cmd+tlinuxctrl+meta+t
    • 快捷鍵不可用極可能是被佔用了,參考這裏
    • 可自定義默認參數

  • Better Comments 經過使用警報,信息,TODO 等進行註釋來改善代碼註釋。使用此擴展,您將可以將註釋分類爲:
    • 快訊
    • 查詢
    • 待辦事項
    • 強調
    • 註釋掉的代碼也能夠設置樣式,以使代碼不該該存在
    • 可自定義指定其餘所需的註釋樣式

  • TODO Highlight 突出顯示TODO,FIXME和任何關鍵字
    • 高亮內置關鍵字,可經過自定義設置覆蓋外觀
    • 也可自定義關鍵字

用事實說話

口說無憑,眼見爲實。下面咱們看下實際開發中的具體狀況:

  • 沒有註釋
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: '錯誤提示',
      content: `「${itemNames.join(',')}」庫存信息未完善,請完善庫存信息`,
    });
  }
複製代碼
  • 通常般的註釋
// 遍歷當前全部選中的sku,查找出沒有庫存的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的庫存爲空時進入校驗
if (noStockItemIds.length > 0) {
  const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);
  return Modal.warning({
    title: '錯誤提示',
    content: `「${itemNames.join(',')}」庫存信息未完善,請完善庫存信息`,
  });
}
複製代碼
  • 更好的註釋
// 遍歷當前全部選中的sku,查找出沒有庫存的itemId
const noStockItemIds = beSelectSkucontainer.reduce((arr, itemId) => {
    // selectRowskey是一個對象,以itemId爲key,sku對象做爲value,sku對象以skuId做爲key,sku做爲value,只有selectRowskey下全部itemId下的sku都有庫存纔算校驗經過
    /*         數據格式:         selectRowskey: {           12345678: { // itemId               123456: { // skuId               name: 'sku',               }           }         }       */
    const res = Object.keys(selectRowskey[itemId]).every((skuId) => {
        const sku = selectRowskey[itemId][skuId];
        return !!sku.stockQuantity;
    });
    // 只要有一條sku沒有庫存時,就塞到arr中,返回給noStockItemIds數組
    if (!res) {
        arr.push(itemId);
    }
    return arr;
}, []);
// 有一條sku的庫存爲空時進入校驗
if (noStockItemIds.length > 0) {
    // 根據id查找商品名稱
    const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);
    Modal.warning({
        title: '錯誤提示',
        content: `「${itemNames.join(',')}」庫存信息未完善,請完善庫存信息`,
    });
}
複製代碼

看到上面這段代碼能夠很明顯的體會到有沒有註釋以及註釋寫的清不清楚的重要性。如果寫了註釋但仍然看不懂,那還不如不寫。

因此註釋也不是隨便寫一寫就能夠的,要描述某段代碼的功能,註明邏輯,讓開發者能夠」無腦「瀏覽。

以前在工做羣中看到有人發過這樣一張圖(以下圖),我的認爲是一個很好的代碼註釋的範例:

結語

看到這裏,對於註釋的重要性各位已經有本身的認知。還有幾點是咱們寫註釋時須要注意的:

  • 註釋內容要簡潔、清楚明瞭。註釋簡述功能或實現邏輯便可,無需每行代碼都添加註釋

  • 代碼如有修改,切記同步修改對應的註釋。不要出現過時的註釋,不然會起到副作用

    有任何意見歡迎下方評論區留言討論~

參考文獻

爲何要寫註釋 js/javascript代碼註釋規範與示例 代碼中的特殊註解 -- TODO、FIXME、XXX的做用 註釋的做用, 以及如何寫註釋 你肯定Java註釋不會被執行嗎?80%的人都不知道IE 瀏覽器條件註釋詳解 關於CSS中對IE條件註釋的問題

推薦閱讀

個人前端職業進階之路

淺談 React 中的 XSS 攻擊

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 40 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com

相關文章
相關標籤/搜索