1. 程式人生 > >3、學什麼技術之前端開發JS程式碼規範語法

3、學什麼技術之前端開發JS程式碼規範語法

學什麼技術之前端開發JS程式碼規範語法

JS程式碼規範一(語法&格式篇)

基本原則

  1. 所有的程式碼都要符合可維護性原則 —— 簡單、便於閱讀。

  2. 部分編碼原則是與效能原則相悖的, 如果遇到這種情況, 請優先遵守語法規範。 (注: 如果確實有不確定的 情況或者效能影響很大, 請聯絡你的主管或者我, 我們共同來商量最佳的解決方案)

檔案屬性

  1. 所有 js 檔案均以 UTF-8 作為預設編碼, 不能含有 BOM(Byte Order Mark, 是 UTF 編碼方案裡用於標識編 碼的標準標記)頭。

  2. 檔案命名, 必須是有意義的檔名; 建議使用英文或數字組合; 檔名中嚴禁出現中文; 多個英文單詞請用駝峰 命名法,如:historyManager.js

  3. 請不要在檔名後加數字, 來實現相同功能的多個版本, 如:drag1.js、drag2.js、drag3.js是禁止的。

  4. 上線到公網的檔名及檔案路徑, 避免一些特殊字元, 如:ad、64, 免被某些瀏覽器或者防火牆遮蔽。

  5. 所有上線程式碼及原始碼必須納入到 git 管理, 避免離職或者變動崗位, 造成程式碼丟失後期不可維護。

  6. (建議) 不使用(漢語拼音/拼音英文數字組合/不規範的英文縮寫)作為檔名。例如: jubao.js, MseEvtHdlr.js, mendian-address.js。

  7. (建議) 儘量使用英文小寫作為檔名, 免某些系統不區分檔名大小寫。

  8. (建議) 檔案的目錄規劃及歸屬, 遵守各專案的具體規定。如果是新專案, 請參照主站的目錄結構管理辦法。

程式碼格式化

換行原則

  1. (建議)每行程式碼不要超過 80 字元。當一條語句一行寫不下時, 折行。

  2. 折行位置:在運算子號後面換行。在運算子後換行可以減少因為複製貼上產生的錯誤被分號掩蓋的機率。

  3. 每行程式碼只允許一個有效語句, 止為了減少程式碼行數, 多個語句放在一行程式碼中。

  4. 多行字串使用 + 拼接形式換行(打包工具會優化它, 用擔心效能問題), 不要使用 \ 拼接(它不是 ECMA 的 標準規範)。 例如, 下面的程式碼是錯誤的(在編譯時, 不能忽略行起始位置的空白字元):

    var wrongString = 'The string is so long, \
                      we need split \
    in multi-line.';

    正解處理長串:

    var wrongString = '<div>' +
                         '<header>title text</header>' +
                         '<p>some description text</p>' +
                      '</div>';
  5. "{}" (大括號)前面不需要換行, 例如函式定義、if 語句、while 語句、switch 等。

縮排原則

  1. 程式碼需要保持適當的縮排。

  2. 縮排請使用Tab縮排(1 tab = 4 spaces,可以在編輯器設定)空格縮排顯示以相容不同IDE, 不同操作平臺的差異。

  3. 複合語句(被包含在 “{}”(大括號)的語句序列, 包括函式定義)需要保持縮排。

  4. 初始化陣列和 JSON 物件時, 如果初始值較長, 需要換行並保持縮排。

  5. 函式呼叫時, 如果傳遞的是匿名函式, 匿名函式的內容需要換行並保持縮排, 讓匿名函式更加易讀。

空格

需要保留空格的情況:

  1. 各種運算子, 包括數值操作符(例如:+、-、*、/、%等)、位運算子、比較運算子、三元運算子、複合賦值運算 符、賦值運算子前後, 保留一個空格。 “.”(點) 和“(”(左括號)和 “[”(左方括號)例外。

  2. for 迴圈條件中, 分號後保留一個空格。

  3. 所有的逗號後保留一個空格, 例如: 變數宣告語句、陣列值、JSON 物件值、函式引數值等等。

  4. 冒號前後都加空格。

不需要保留空格的情況:

  1. 一元操作符與其運算元之間不應有空格, 如: i++。除非操作符是個單詞, 例如: typeof window。

  2. 點號前後不要出現空格。

  3. 空行不要有空格, 尾不要有空格。

  4. 空物件和陣列不需要填入空格。[], {}

  5. 函式呼叫的括號“(”前, 要出現空格。

  6. 註釋符前後要有空格。

空行

  1. 邏輯上獨立的程式碼塊, 物件方法,函式前後使用空行分隔。儘量使用空行將邏輯相關的程式碼塊分割開, 以提高程式的可讀性。

  2. 連續空行數控制在 1 ~ 3 行(根據程式碼塊層次來控制空行數)。

  3. (建議) 檔案末尾留 1 ~ 2 個空行。

語句行

  1. 語句必須以分號作為結束符, 不能忽略分號。除了 for, function, if, switch, try, while 結束的花括號後無需分號。

    js 的語句以分號作為結束符, 除非可以非常準確推斷某結束位置才會省略分號。上面的幾個例子產出錯誤, 均是在語句中聲明瞭函式/物件/陣列直接量, 但閉括號 ( '}' 或 ']' ) 並不足以表示該語句的結束。 在js 中, 只有當語句後的下一個符號是字尾或括號運算子時, 才會認為該語句的結束

    有的情況下, 漏掉分號會有大麻煩, 例如:

    # 情況一: 語句會解釋成, 一個函式帶一匿名函式作為引數而被呼叫, 返回 42 後, 又一次被呼叫, 這就導致了錯誤

    MyClass.prototype.myMethod = function() {
        return 42;
    } // 缺分號
    
    (function() {
        // Some initialization code wrapped in a function to create a scope for locals.
    })();

    # 情況二: 在執行時遇到 'no such property in undefined' 錯誤, 原因是程式碼試圖這樣 x[ffVersion][isIE]() 執行

    var x = {
        'i': 1,
        'j': 2
    } // 缺分號
    [ffVersion][isIE]();

    # 情況三: 當 resultOfOperation() 返回非 0 時, 就會呼叫 die, 其結果也會賦給 arrResult

    var arrResult = ['1', '2', '3'] // 缺分號
    
    // conditional execution a la bash
    -1 === resultOfOperation() || die();
  2. 對於複合語句, if, for, while, do, switch, try ... catch 等程式碼體, 數定義的函式體, 物件的定義等 都需要放在花括號 “{}” 裡面。

    • “{” 應在行末, 標誌程式碼塊的開始。
    • “}” 應在一行開頭, 標誌程式碼塊的結束, 同時需和“{”所在行的開始對齊, 以表明是一個完整的複合語句。這 樣可極大地提高程式碼的可閱讀性, 控制邏輯能清晰地表現出來。
    • 被包含的程式碼段應該保持縮排。
    • 即使被包含的程式碼段只有一句, 也應該用“{}”包含。儘管不用花括號程式碼也不會錯, 但如若需要增加語句的 話, 則較容易因花括號遺漏而引起的編譯錯誤或邏輯錯誤。
  3. return 語句在使用時也需慎重, 如果用表示式的執行作為返回值, 請把表示式和 return 放在同一行中, 以 免換行符被誤解析為語句的結束而引起返回錯誤。 例如:

    function unexpectedReturn() {
        var valueA = 1, valueB = 2;
        return
            valueA + valueB;
    }
    console.log(unexpectedReturn()); // ouput: undefined

程式碼格式示例

function walk(holder, key) {

    // The walk method is used to recursively walk the resulting structure so
    // that modifications can be made.

    var k, v, value = holder[key];
    if (value && typeof value === 'object') {
        for (k in value) {
            if (Object.prototype.hasOwnProperty.call(value, k)) {
                v = walk(value, k);
                if (v !== undefined) {
                    value[k] = v;
                } else {
                    delete value[k];
                }
            }
        }
    }

    return reviver.call(holder, key, value);
}

程式碼註釋

註釋,給以後需要理解你的程式碼的人(或許就是你自己)留下資訊是非常有用的。註釋應該和它們所註釋的代 碼一樣是書寫良好且清晰明瞭。 避免冗長或者情緒化的註釋。

檔案頭部註釋

  1. 所有 js, css 檔案頭部, 必須有檔案註釋, 用於簡要說明: 檔案的主要功能(含模組資訊)、作者(含作者郵箱)、版權等資訊。 檔案頭註釋格式如下:

    /**
     * Basic lang utilities functions ...
     *
     * @author Allex Wang
     * @module clib/lang
     */
    define('lang', function(require, exports, module) {
      ...
    });
  2. 註釋需要簡潔明瞭, 從已解決的方案到未開發的功能, 註釋必須與程式碼相關。

  3. 及時地更新註釋, 錯誤的註釋會讓程式更加難以閱讀和理解。

  4. (建議) 公用模組檔案,請新增範例程式碼。

  5. (建議) 所有的註釋請儘量使用英文(跨平臺編輯)。

  6. (建議) // 用作程式碼行註釋, /* ... */ 形式用作對整個程式碼段的登出,或較正式的宣告中, 如函式引數、功能、檔案功能等的描述中。

  7. (建議) 如果可能, 檔案頭部的註釋, 還要包含檔案依賴關係、版本號、第三方程式碼來源url資訊等。

程式碼內部註釋

  1. 大量的變數聲明後面須添加註釋, 說明變數用途。

  2. 每個對外暴露的 API 方法, 需要加註釋說明其用途。註釋需要包括: 函式的用途、引數、返回值等, 此三項為強制要求。

  3. 生澀的程式碼就沒有必要添加註釋了, 首先您需要重寫它們。

  4. 註釋沒有必要每行都新增, 只在重要的邏輯、或者較複雜的邏輯處, 增加必要的註釋。

  5. 通俗易懂的語句壓根兒不需要添加註釋,合理的變數命名其實是最直接的註釋。

  6. 刪除註釋掉的程式碼塊, 只要提交了 git, 程式碼可以隨時找回, 無需保留被註釋的廢棄程式碼。

模組註釋規範

  1. 類註釋
每個類的定義都要附帶一份註釋, 描述類的功能和用法. 也需要說明構造器引數. 如果該類繼承自其它類,
應該使用 @extends 標記.  如果該類是對介面的實現, 應該使用 @implements 標記.

  ```javascript
  /**
   * Class making something fun and easy.
   * @param {String} arg1 An argument that makes this more interesting.
   * @param {Array} arg2 List of numbers to be processed.
   * @constructor
   * @extends {goog.Disposable}
   */
  project.MyClass = function(arg1, arg2) {
      // ...
  };
  goog.inherits(project.MyClass, goog.Disposable);
  ```
  1. 方法與函式的註釋
提供引數的說明. 使用完整的句子, 並用第三人稱來書寫方法說明.

  ```javascript
  /**
   * Converts text to some completely different text.
   * @param {String} arg1 An argument that makes this more interesting.
   * @return {String} Some return value.
   */
  MyClass.prototype.someMethod = function(arg1) {
      // ...
  };

  /**
   * Operates on an instance of MyClass and returns something.
   * @param {project.MyClass} obj Instance of MyClass which leads to a long
   *     comment that needs to be wrapped to two lines.
   * @return {boolean} Whether something occured.
   */
  function PR_someMethod(obj) {
      // ...
  }
  ```

對於一些簡單的, 不帶引數的 getters, 說明可以忽略.

  ```javascript
  /**
   * @return {Element} The element for the component.
   */
  Component.prototype.getElement = function() {
      return this._element;
  };
  ```
  1. 屬性註釋

    /**
     * Maximum number of things per pane.
     * @type {Number}
     */
    project.MyClass.prototype.someProperty = 4;
  2. 型別轉換的註釋

有時, 型別檢查不能很準確地推斷出表示式的型別,
所以應該給它新增型別標記註釋來明確之, 並且必須在表示式和型別標籤外面包裹括號.

  ```javascript
  function setFoo(x) (/* @type {Number} */ x) { ... }
  ```
  1. 列舉

    /**
     * Enum for tri-state values.
     * @enum {Number}
     */
    project.TriState = {
        TRUE: 1,
        FALSE: -1,
        MAYBE: 0
    };
注意一下, 列舉也具有有效型別, 所以可以當成引數型別來用.

  ```javascript
  /**
   * Sets project state.
   * @param {project.TriState} state New project state.
   */
  project.setState = function(state) {
      // ...
  };
  ```
  1. JSDoc 縮排
如果你在 @param, @return, @supported, @this 或 @deprecated 中斷行, 需要像在程式碼中一樣,
使用4個空格作為一個縮排層次.

  ```javascript
  /**
   * Illustrates line wrapping for long param/return descriptions.
   * @param {String} foo This is a param with a description too long to fit in
   *     one line.
   * @return {Number} This returns something that has a description too long to
   *     fit in one line.
   */
  project.MyClass.prototype.method = function(foo) {
      return 5;
  };
  ```
  1. 模組使用範例註釋:

    /**
     * @example
     *  var bleeper = makeBleep(3);
     *  bleeper.flop();
     */
  2. Typedefs:

有時型別會很複雜. 比如下面的函式, 接收 Element 引數

  ```javascript
  /**
   * @param {String} tagName
   * @param {String|Element|Text|Array} contents
   * @return {Element}
   */
  goog.createElement = function(tagName, contents) {
      ...
  };
  ```

你可以使用 @typedef 標記來定義個常用的型別表示式

  ```javascript
  /** @typedef {String|Element|Text|Array} */
  goog.ElementContent;

  /**
   * @param {String} tagName
   * @param {goog.ElementContent} contents
   * @return {Element}
   */
  goog.createElement = function(tagName, contents) {
      ...
  };
  ```

命名規範

變數命名

  1. 宣告變數必須加上關鍵字 var, 避免使用未宣告的變數。
當你沒有寫 var, 變數就會暴露在全域性上下文中, 這樣很可能會和現有變數衝突。另外, 如果沒有加上,
很難明確該變數的作用域是什麼, 變數也很可能看起來像在區域性作用域中, 但實際上,
已經洩漏到 window 中, 所以務必用var 去宣告變數 (批量定義變數一定要注意結束符逗號 "," 不能漏掉)。
  1. 精簡短小, 見名知意。變數名使用英文大小寫和數字命名, 使用駝峰命名規則, 例如: getStyle、addEvent。

  2. 變數的命名, 不得使用 js 保留字。 js 保留字列表: abstract boolean break byte case catch char class const continue default do double else extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try var void while with

  3. 不能使用沒有任何意義的變數名 (迴圈的指標變數除外, 見下文 15), 例如: var a = 1; var xx = true; 避免無意義的簡寫, 例如: MouseEventHandler 寫作 MseEvtHdlr。

  4. 常量、列舉量使用全大寫作為變數名, 多個單詞之間用下劃線(_)分隔。例如: NAME_LIKE_THIS。

  5. 除非必要, 請不要使用全域性變數, 避免出現全域性變數汙染。(特殊情況需要經專案前端技術負責人審批)

  6. 函式內部的變數, 請在函式定義的頭部宣告。出於避免造成不必要消耗的考慮, 複雜型別的變數可以只宣告不賦值。唯一特殊的情況, 是 for 迴圈的下標變數, 可以使用的時候實時宣告。

  7. 函式內儲存 DOM 引用和定時器的變數, 使用完後必須顯式銷燬, 從而可以及時的執行記憶體回收。例如設定該變數為 null; 定時器變數銷燬, 請執行 clearInterval 或者 clearTimeout。

  8. 函式內兩次以上使用到的同一全域性變數或者外部物件, 定義為一個區域性變數, 以保證程式效能最優, 例如: var doc = document, win = window;

  9. 私有化變數和方法名應該以下劃線 _ 開頭 (僅限有跨作用域的變數或方法等).

  10. (建議) 不在變數名前加下劃線 _ 表示私有變數。

  11. (建議) this 命名, 儘量以能表達當前例項意義的名稱, 通用命名為 self;

```javascript
Pager.protoype.goto = function() {
    var pager = this, index = page.currentIndex + 1;
    setTimeout(function() { page.doRequest(index); });
};
function foo1(){
    var self = this;
    setTimeout(function() { console.log(self.name); }, 1);
}
function foo2(name){
    var self = this;
    self.name = name;
    foo1.call(self);
}
```
  1. (建議) 布林變數、返回值為 boolean 的函式/方法前可以新增字首 is/has/can/should。

  2. (建議) 避免產生歧義的命名, 例如: isNotError, isNotFound。

  3. (建議) 表示錯誤的變數, 建 for 迴圈中的臨時重複變數建議以 i, j, k, m, n ... 命名。

  4. (建議) 多個變數宣告時, 適當換行表示, 參照 var 關鍵字位置保持縮排。

自定義物件(類)命名

  1. 自定義物件(類)
命名, 每個單詞首字母均需要大寫, 例如: ModuleDialog。
內部物件  (不會匯出的構造器或靜態物件), 使用 _ 開頭來定義, 例如: _BaseTab;
  1. 物件的方法
駝峰命名方式, 必須是動詞或者動詞短語, 例如: obj.getSomeValue()。
函式的引數個數不固定時, 應該新增最後一個取名為 `args` 的引數.
可選和可變引數應該在 @param (Optional) 標記中說明清楚.

變數賦值及定義

  1. 下面型別的物件不建議用 new 構造,而是使用直接量賦值: new Number, new String, new Boolean, new Object (用{}代替), new Array(用[]代替)。

  2. 禁止陣列或者 JSON 中出現冗餘的逗號, IE 下會丟擲語法錯誤。 例如: var testArray = [1,2,3,]; 或者 var jsonExample = { 'key1': value1, 'key2': value2,};

  3. 禁止汙染內建物件的原型, 例如 Object.prototye、Array.prototype、Function.prototype。

  4. 不要使用連等進行賦值。例如:

```javascript
(function () {
    var numberA = numberB = 3; // 這裡產生的 numberB 是全域性變數
})();
alert(numberB); // return 3
```
  1. 如果一個賦值語句是用函式和物件來賦值, 可能需要跨多行, 一定切記要在賦值語句末加上分號。

  2. (建議) 陣列和物件初始化時, 如果初始值不是很長, 儘量保持寫在單行上; JSON 物件中, 比較長的 key/value, 不必為了美觀以冒號對齊。

  3. (建議) 沒必要在每次宣告變數時就將其初始化, 但使用變數之前一定要確保變數已經初始化。

  4. (建議) 字串變數賦值的時候, 優先使用單引號, 而不是雙引號, 尤其是你建立的是 HTML 字串。

```javascript
var s = 'hello world';
var html = '<div class="header"></div>';
console.log(s + ', my name is cat');
console.log(html);
```

條件語句/迴圈

條件語句

  1. if 語句, 即使只有單行, 也要用花括號括起來, 例如:
```javascript
// (錯誤)
if (condition) statement;

// (正確)
if (condition) {
  statement;
}
```
  1. 使用三元運算子, 替代單一的 if else 語句。例如:
```javascript
if (val != 0) {
     return foo();
} else {
    return bar();
}

// 可以寫作:
return val ? foo() : bar();
```
  1. if/else/while/for 條件表示式必須有小括號, 且自佔一行。

  2. (建議) 利用 && 和 || 短路來簡化程式碼:

```javascript
function foo(opt_win) {
    var win;
    if (opt_win) {
        win = opt_win;
    } else {
        win = window;
    }
    // ...
}

// 可以寫作:
function foo(opt_win) {
    var win = opt_win || window;
    // ...
}
```

&& 短路:

```javascript
if (node) {
    if (node.kids) {
        if (node.kids[index]) {
            foo(node.kids[index]);
        }
    }
}

// 可以寫作:
if (node && node.kids && node.kids[index]) {
    foo(node.kids[index]);
}

// 或者
var kid = node && node.kids && node.kids[index];
if (kid) {
    foo(kid);
}
```
  1. (建議) 使用嚴格的條件判斷符。用 === 代替 ==, 用!== 代替 !=。

迴圈

  1. 儘量避免 for-in 迴圈, 只用於 object/hash 的遍歷, 陣列的遍歷使用 for 迴圈。

  2. for-in 迴圈體中必須用 hasOwnProperty 方法檢查成員是否為自身成員, 避免來自原型鏈上的汙染。

  3. 避免在 if 和 while 語句的條件部分進行賦值。例如:

```javascript
// (錯誤)
var i = 10;
while (i = i - 2) {
    statement;
}
// 應該寫作: (正確)
var i = 10;
while (i > 0) {
    statement;
    i = i - 2;
}
```

函式/閉包

函式

  1. 一個函式的內容不宜太長, 較複雜的邏輯, 需拆分成多個函式來實現, 使程式碼邏輯清晰。

  2. 對外暴露的 API 型函式, 儘量保持輸入輸出的穩定, 減小呼叫者修改程式碼的成本和風險。

  3. 可以巢狀函式, 用於減少重複程式碼, 隱藏一些區域性函式等, 但不要在塊內宣告一個函式。因為 JS 並不支援塊級作用域, 雖然很多 js 引擎都支援塊內宣告函式, 但它不屬於 ECMAScript 規範 (見 ECMA-262, 第 13 和 14 條)。各個瀏覽器糟糕的實現相互不相容, 有些也與未來 ECMAScript 草案相違背。ECMAScript 只允 許在指令碼的根語句或函式中宣告函式. 如果確實需要在塊中定義函式, 建議使用函式表示式來初始化變數。例如:

```javascript
// (錯誤)
if (x){
    function foo () {}
}
// 應該寫作: (正確)
if (x) {
    var foo = function () {};
}
```
  1. 有很多方法可以給構造器新增方法或成員, 我們更傾向於使用如下的形式:
```javascript
Foo.prototype.bar = function() {
    /* ... */
};
```
  1. 僅在物件構造器、方法、閉包中使用 this 物件, 避免 this 亂用出現的指代不明。 this 的語義很特別。有時它引用一個全域性物件(大多數情況下), 呼叫者的作用域(使用 eval 時), DOM 樹中的節點(新增事件處理函式時), 新建立的物件(使用一個構造器), 或者其他物件(如果函式被 call() 或 apply())。

  2. 引用物件成員用 obj.propName 代替 obj['propName'], 除非屬性名是變數或是介面資料的引用。

函式引數

  1. 函式的必選引數, 必須檢查是否傳遞了合法的引數, 避免參數型別不對帶來的異常。

  2. 函式引數寫在同一行上。如果一行超過 80 字元, 請按照縮排原則進行換行, 並保持適當縮排。

閉包

  1. 由於閉包保留了一個指向它封閉作用於的指標, 所以在給 DOM 元素附加閉包時候, 要避免產生迴圈引用, 從而導致記憶體洩露。例如:
```javascript
// (錯誤)
function foo(element, a, b) {
    element.onclick = function () { /* 使用變數 a 和 b */ }
}
```

這裡, 即使沒有使用 element, 閉包也保留了 element, a 和 b 的引用, 由於 element
也保留了對閉包的引用, 這就產生了迴圈引用, 這就不能被 GC 回收. 這種情況下, 可將程式碼重構為:

```javascript
function foo(element, a, b) {
    element.onclick = handle(a, b);
}
function handle(a, b) {
    return fun