JavaScript編碼風格指南

funnycoderstar發表於2019-02-24

首次發表在個人部落格

前言

程式語言的編碼風格指南對於一個長期維護的軟體而言是非常重要的;好的程式設計風格有助於寫出質量更高、錯誤更少、更易於 維護的程式。

團隊合作需要制定一些程式碼規範還有利用一些工具來強制要求團隊程式碼的風格統一.畢竟很多情況下以後不一定是由寫一手程式碼的人來維護程式碼,所以有一個統一的程式碼風格很重要!!!

最近看了一下編寫可維護的JavaScript編寫高質量程式碼:Web前端開發修煉之道,根據書中提倡的一些寫法,同時結合我個人的經驗和喜好做了一些改動,大致整理了如下JavaScript編碼風格

JavaScript編碼風格

1.縮排

每一行的層級由4個空格組成,避免使用製表符(Tab)進行縮排

if (true) {
    doSomething();
}
複製程式碼

2.行的長度

每行長度不應該超過80個字元.如果一行多於80個字元,應當在一個運算子(逗號,加好等)後換行.下一級應當增加兩級縮排(8個字元).

// 好的寫法
doSomething(arg1, arg2, arg3, arg4,
        arg5);

// 不好的寫法: 第二行只有4個空格的縮排
doSomething(arg1, arg2, arg3, arg4,
    arg5);
// 不好的寫法: 在運算子之前換行
doSomething(arg1, arg2, arg3, arg4
        ,arg5);
複製程式碼

3.原始值

特殊值null除了下述情況應當避免使用

  • 用來初始化一個變數,這個變數可能被賦值為一個物件
  • 用來和一個已經初始化的變數比較,這個變數可以是也可以不是一個物件
  • 當函式的引數期望是物件時,被用作返回值傳出
// 好的做法
const person = null;
複製程式碼

判斷一個變數是否定義應當使用 typeof 操作符

// 好的寫法
if (typeof constiable == `undefined`) {
    // do something
}

// 不好的寫法
if (constiable == `undefined`) {
    // do something
}
複製程式碼

4.運算子間距

二元運算子前後必須使用一個空格來保持表示式的整潔.操作符包括賦值運算子和邏輯運算子

// 好的寫法
const found = (values[i] === item);
// 不好的寫法: 丟失了空格
const found = (values[i]===item);

// 好的寫法
if (found && (count > 10)) {
    doSomething();
}
// 不好的寫法: 丟失了空格
if (found&&(count>10)) {
    doSomething();
}

// 好的寫法
for(let i = 0; i < count; i++) {
    process(i);
}
// 不好的寫法: 丟失了空格
for(let i=0; i<count; i++) {
    process(i);
}
複製程式碼

5.括號間距

當使用括號時,緊接左括號之後和緊接右括號之前不應該有空格

// 好的寫法
const found = (values[i] === item);
// 不好的寫法: 左括號之後有額外的空格
const found = ( values[i] === item);

// 好的寫法
if (found && (count > 10)) {
    doSomething();
}
// 不好的寫法: 右括號之後有額外的空格
if (found && (count > 10) ) {
    doSomething();
}

// 好的寫法
for(let i = 0; i < count; i++) {
    process(i);
}
// 不好的寫法: 引數兩邊有額外的空格
for(let i = 0; i< count; i++) {
    process( i );
}
複製程式碼

6.物件直接量

物件直接量應當使用如下格式

  • 起始左花括號應當同表示式保持同一行
  • 每個屬性的名值對應當保持一個縮排,第一個屬性應當在左花括號後另起一行.
  • 每個屬性的名值對應當使用不含引號的屬性名,其後緊跟一個冒號(之前不含空格),而後是值
  • 倘若屬性值是函式型別,函式體應當在屬性名之下另起一行,而且其前後均應保留一個空行
  • 一組相關的屬性前後可以插入空行以提高程式碼的可讀性
  • 結束的右花括號應當獨佔一行
// 好的寫法
const object = {

    key1: value1,
    key2: value2,

    func: function() {

    },

    key3: value3,
};

// 不好的寫法: 不恰當的縮排
const object = {
        key1: value1,
        key2: value2,
    };

// 不好的寫法:函式體缺少空行
const object = {

    key1: value1,
    key2: value2,
    func: function() {

    },
    key3: value3,
};
複製程式碼

當物件字面量作為函式引數時,如果值是變數,起始花括號應當同函式名在同一行.所有其餘先前列出的規則同樣適用

// 好的寫法
doSomething({
    key1: value1,
    key2: value2,
});

// 不好的寫法
doSomething({ key1: value1, key2: value2 });
複製程式碼

7.註釋

頻繁地適用註釋有助於他人理解你的程式碼.如下情況應當使用註釋

  • 程式碼晦澀難懂
  • 可能被誤認為錯誤的程式碼
  • 必要但不明顯的針對特定瀏覽器的程式碼
  • 對於物件,方法或者屬性,生成文件是有必要的(使用恰當的文件註釋).

1).單行註釋

使用單行註釋當用來說明一行程式碼或者一組程式碼.單行註釋可能有三種使用方式

  • 獨佔一行的註釋,用來解釋下一行程式碼
  • 在程式碼行的尾部的註釋,用來解釋它之前的程式碼
  • 多行,用來註釋掉一個程式碼塊
// 好的寫法
if (condition) {

    // 如果程式碼執行到這裡,則說明通過了所有的安全性檢測
    allowed();
}

// 不好的寫法:註釋之前沒有空行
if (condition) {
    // 如果程式碼執行到這裡,則說明通過了所有的安全性檢測
    allowed();
}

// 不好的寫法: 錯誤的縮排
if (condition) {

// 如果程式碼執行到這裡,則說明通過了所有的安全性檢測
    allowed();
}

// 不好的寫法: 這裡應當用多行註釋
// 接下來的這段程式碼非常難, 那麼,讓我詳細的解釋一下
// 1. xxxx
// 2. xxxx
if (condition) {

// 如果程式碼執行到這裡,則說明通過了所有的安全性檢測
    allowed();
}
複製程式碼

對於程式碼行尾單行註釋的情況,應確保程式碼結尾同註釋之間至少一個縮排

// 好的寫法
const result = something + somethingElse; // somethingElse will never be null

// 不好的寫法: 程式碼和註釋間沒有足夠的空格
const result = something + somethingElse;// somethingElse will never be null
複製程式碼

註釋一個程式碼塊時在連續多行使用單行註釋是唯一可以接受的情況.多行註釋不應當在這種情況下使用

// 好的寫法
// if(condition) {
//     doSomething();
// }
複製程式碼

2).多行註釋

多行註釋應當在程式碼需要更多文字去解釋的時候使用.每個多行註釋都至少有如下三行.
1.首行僅僅包括 /* 註釋開始.該行不應當有其他文字
2.接下來的行以 * 開頭並保持左對齊.這些行可以由文字描述
3.最後一行以 */開頭並同先前行保持對齊.也不應當有其他文字

多行註釋的首行應當保持同它描述程式碼的相同層次的縮排.後續的每行應當有同樣層次的縮排並附加一個空格(為了適當保持 * 字元的對齊).每一個多行程式碼之前應當預留一個空格

// 好的寫法
if (condition) {

    /*
     * 如果程式碼執行到這裡
     * 說明通過了所有的安全性檢測
    */
    allowed();
}

// 不好的寫法: 註釋之前無空行
if (condition) {
    /*
     * 如果程式碼執行到這裡
     * 說明通過了所有的安全性檢測
    */
    allowed();
}
// 不好的寫法: 星號後沒有空格
if (condition) {

    /*
     *如果程式碼執行到這裡
     *說明通過了所有的安全性檢測
    */
    allowed();
}
// 不好的寫法: 錯誤的縮排
if (condition) {

/*
 * 如果程式碼執行到這裡
 * 說明通過了所有的安全性檢測
*/
    allowed();
}

// 不好的寫法: 程式碼尾部註釋不要用多行註釋格式
const result = something + somethingElse; /* somethingElse 不應當取值為null */
複製程式碼

3)註釋宣告

註釋有時候可以用來給一段程式碼宣告額外的資訊.這些宣告的格式以單個單詞打頭並緊跟一個雙引號.可使用的宣告如下

  • TODO: 說明程式碼還未完成.應當包含下一步要做的事情
  • HACK: 表明程式碼實現走了一個捷徑
  • XXX: 說明程式碼是有問題的並應當儘快修復
  • FIXME: 說明程式碼是有問題的並應當儘快修復.重要性略次於XXX
  • REVIEW: 說明程式碼任何可能的改動都需要評審
    這些宣告可能在一行或多行註釋中使用,並且應當遵循同一般註釋型別相同的格式規則
// 好的寫法
// TODO: 我希望找到一種更快的方式
doSomething();
// 不好的寫法: 註釋宣告空格不正確
// TODO :  我希望找到一種更快的方式
doSomething();

// 好的寫法
// REVIEW: 有更好的方法嗎?
doSomething();
// 不好的寫法: 程式碼和註釋應當保持同樣的縮排
    // REVIEW: 有更好的方法嗎?
doSomething();
複製程式碼

8.命名

變數命名應當採用駝峰命名格式,首字母小寫,每個單詞首字母大寫.變數名的第一個單詞應當是一個名詞(而非動詞)比避免同函式混淆.不要在變數名中使用下劃線

// 好的寫法
const myName = `Jack`;

// 不好的寫法: 大寫字母開頭
const MyName = `Jack`;

// 不好的寫法: 動詞開頭
const getMyName = `Jack`;

// 不好的寫法: 使用下劃線
const my_name = `Jack`;
複製程式碼

函式命名應當採用駝峰命名格式.函式名的第一個單詞應當是動詞(而非名詞)來避免同變數混淆.函式名中最好不要使用下劃線.

// 好的寫法
function doSomething() {
    // 程式碼
}

// 不好的寫法: 大寫字母開頭
function DoSomething() {
    // 程式碼
}
// 不好的寫法: 名詞開頭
function car() {
    // 程式碼
}
// 不好的寫法: 使用下劃線
function do_something() {
    // 程式碼
}
複製程式碼

建構函式–通過new元素安撫建立新物件的函式–也應使用駝峰合適命名,首先首字母大寫.建構函式命名應當以非動詞開頭,因為new代表著建立一個物件例項的操作

// 好的寫法
function MyObject() {

}
// 不好的寫法: 小寫字母開頭
function myObject() {
    
}
// 不好的寫法: 使用下劃線
function My_Object() {
    
}
// 不好的寫法: 動詞開頭
function getMyObject() {
    
}
複製程式碼

常量(不會被改變的變數)的命名應當是所有字母大寫,不同單詞之間用單個下劃線隔開.ES6中使用const來宣告一個常量

// 好的寫法
const TOTAL_COUNT = 10;
// 不好的寫法
const totalCount = 10;
// 不好的寫法: 混合模式
const total_COUNT = 10;
複製程式碼

物件的屬性同變數的命名規範相同.物件的方法同函式的命名規則相同.如果屬性或者方法是私有的,應當在之前加一個下劃線

// 好的寫法
const object = {
    _count: 10,

    _getCount: function() {
        return this._count;
    }
}
複製程式碼

9.賦值

當給變數賦值時,如果右側是含有比較語句的表示式,需要用圓括號包裹

// 好的寫法
const flag = (i < count);

// 不好的寫法:遺漏圓括號
const flag = i < count;
複製程式碼

10.等號運算子

使用 === (嚴格相等) 和 !==(嚴格不相等)代替 ==(相等) 和 !=(不等) 來避免弱型別轉換錯誤

// 好的寫法
const same = (a === b);

// 不好的寫法: 使用 == 
const same = (a == b);
複製程式碼

11.三元操作符

三元運算子應當僅僅用在條件賦值語句中,而不要作為if語句的替代品.

// 好的寫法
const value = condition ? value1 : value2;

// 不好的寫法: 沒有賦值,應當使用 if 表示式
condition ? doSomething() : doSomethingElse();
複製程式碼

12.語句

簡單語句

每一行最多隻包含一條語句.所有簡單的語句都應該以分號(;)結束.

// 好的寫法
const a = 1;
const b = 2;
const c = a + b;

// 不好的寫法: 多個表示式寫在一行
const a = 1;const b = 2;const c = a + b;
複製程式碼

返回語句

返回語句當返回一個值的時候不應當使用圓括號包裹,除非在某些情況下這麼做可以讓返回值更容易理解.例如:

return;
return collection.size();
return (size > 0 ? size : defaultSize)
複製程式碼

複合語句

複合語句是大括號括起來的語句列表;

  • 括起來的語句應當較複合語句多縮排一個層級
  • 開始的大括號應當在複合語句所在行的末尾;結束的大括號應當獨佔一行且同複合語句的開始保持同樣的縮排.
  • 當括號時控制結構的一部分時,諸如if或者for語句,所有語句都需要用打括號括起來,也包括單個語句.這個約定使得我們更方便地新增語句而不用擔心忘記加括號而引起bug
  • 像if一樣的語句開始的關鍵詞,其後應當緊跟一個空格,起始大括號應當在空格之後

if語句

if (condition) {
    statements
} else if (condition) {
    statements
} else {
    statements
}
複製程式碼

絕不允許在if語句中省略花括號

// 好的寫法
if (condition) {
    doSomething();
} 
// 不好的寫法: 不恰當的空格
if(condition){
    doSomething();
} 
// 不好的寫法: 遺漏花括號
if (condition) 
    doSomething();

// 不好的寫法: 所有程式碼寫在一行
if (condition) { doSomething(); }
// 不好的寫法: 所有程式碼寫在一行且沒有花括號
if (condition) doSomething();

複製程式碼

for語句

for (initialization; condition; update) {
    statements
}

for (constiable in object) {
    statements
}
複製程式碼

當使用 for-in 語句時,記得使用 hasOwnProperty() 進行雙重檢查來過濾出物件的成員

while語句

while (condition) {
    statements
}
複製程式碼

do語句

do {
    statements
} while (condition)
複製程式碼

switch語句

switch (expression) {
    case expression:
        statements
    default:
        statements
}
複製程式碼

switch下的每一個case都叮噹保持一個縮排.除第一個之外包括default在內的每一個case都應當在之前保持一個空行
每一組語句(除了default)都應當以break, return, throw結尾,或者用一行註釋表示跳過

// 好的寫法
switch (value) {
    case 1:
       /* falls through */
    case 2:
        doSomething();
        break;
    case 3:
       return true;
    default:
       throw new Error(`this should not happen`);
}
複製程式碼

try語句

try {
    statements
} catch (constiable) {
    statements
} finally {
    statements
}
複製程式碼

13.嚴格模式

嚴格模式應當僅限在函式內部使用,千萬不要在全域性使用.

ES6 的模組自動採用嚴格模式,不管你有沒有在模組頭部加上”use strict”;。

14.變數宣告

所有的變數在使用前都應事先定義.變數定義應放在函式開頭.
變數定義前應當初始化,並且賦值操作符應當保持一致的縮排.初始化的變數應當在未初始化變數之前.

推薦使用ES6的letconst來宣告變數

15.函式宣告

函式宣告應當在使用前提前定義.
一個不是作為方法的函式(也就是沒有作為一個物件的屬性)應當使用函式定義的格式(不是函式表示式和Function構造器格式).
函式名和開始圓括號之前不應當有空格.結束的圓括號和右邊的花括號之間應該留一個空格.右側的花括號應當同function關鍵字保持同一行.開始和結束括號之間不應該有空格.引數名之間應當在逗號之後保留一個空格.函式體應當保持一級縮排

// 好的寫法
function doSomething(arg1, agr2) {
    return arg1 + arg2;
}
// 不好的寫法: 第一行不恰當的空格
function doSomething (arg1, agr2) {
    return arg1 + arg2;
}
// 不好的寫法: 
const doSomething = function doSomething(arg1, agr2) {
    return arg1 + arg2;
}
// 不好的寫法: 左側的花括號位置不對
function doSomething(arg1, agr2)
{
    return arg1 + arg2;
}
// 錯誤的寫法: 使用Function構造器
const doSomething = new Function(`arg1`, `agr2`, `return arg1 + arg2`);
複製程式碼

16.留白

在邏輯相關的程式碼塊之間新增空行可以提高程式碼的可讀性

兩行空行許可權在如下情況使用

  • 在不同的原始碼檔案之間
  • 在類和介面定義之間

單行空行許可權在如下情況使用

  • 方法之間
  • 方法中區域性變數和第一行語句之間
  • 多行或單行註釋之前
  • 方法中邏輯程式碼塊之間以提高程式碼的可讀性

空格應當在如下情況中使用

  • 關鍵詞後跟括號的情況應當用空格隔開
  • 引數列表中逗號之後應當保留一個空格
  • 所有的除了點(.)之外的二元運算子,其運算元都應當用空格隔開.單目運算子的運算元之間不應該用空白隔開,諸如一元減號,遞增(++),遞減(–)
  • for語句中的表示式之間應當用空格隔開

17. 需要避免的

  • 切勿使用像String一類的原始包裝型別建立的新物件
  • 避免使用eval()
  • 避免使用with語句.改語句在嚴格模式中不復存在,可能在未來也將去除

使用工具(eslint)來強制約束

eslint 規則

eslint規則在.eslintrc.js中定義,覺得不合理的可以禁掉某條規則,或者有好的建議的也可以新增;
主要注意一下幾條:

  • 程式碼縮排用4空格
  • 語句必須預設後加分號
  • 使用單引號
  • 提交程式碼前將console.log語句刪掉或註釋掉(不然影響其他開發人員除錯)
  • 禁止使用const,使用es6的let,const宣告變數

還有一些情況是不需要檢測的,例如第3方的庫, 框架、元件、ui庫等等,可以將這些檔案放在.eslintignore檔案中,可以忽略eslint的檢測

在檔案頂部加上下面這行,可以禁掉整個檔案的eslint規則

/* eslint-disable */
複製程式碼

pre-commit

程式碼提交之前會強制code-review,不符合規範的不允許提交程式碼
使用方法
1.在命令列安裝

npm i --save-dev pre-commit
複製程式碼

2.在package.json中配置

{
    "scripts": {
        "eslint": "eslint ./ --ext js,vue --ignore-pattern .eslintignore --cache --fix",
        "lint-message": "echo `開始 eslint 檢查, 存在 error 則會拒絕提交`"
    },
    "pre-commit": [
        "lint-message",
        "eslint" // 進行eslint檢查並自動修復一些簡單的格式錯誤
    ],
}

複製程式碼

程式碼提交之前會強制code-review,不符合規範的不允許提交程式碼

如果專案實在沒時間去改的話,可以 git commit -m `XXX` --no-verify 或 git commit -n `xxx`強制提交

小技巧-vscode可以配置儲存自動修復eslint錯誤

vscode安裝eslint外掛,在配置中配置如下

{
     "eslint.autoFixOnSave": true,
     "eslint.enable": true,
     "eslint.options": {
        "extensions": [".js", ".vue", ".jsx"]
     },
     "eslint.validate": [
          {

              "language": "vue",
              "autoFix": true
          },
          {
              "language": "javascript",
              "autoFix": true
          },
          {
              "language": "javascriptreact",
              "autoFix": true
          }
      ],
}

複製程式碼

配置完成之後,每次儲存,都會自動根據 .eslintrc.js檔案自動修復空格,分號的錯誤;但是最好還是在平常的編碼中養成一個良好的習慣,而不是依賴工具.

下列參考給出的文章及書籍,有時間一定要好好看一下,會幫助大家深刻理解JavaScript編碼風格的重要性。永遠記住,規範能解決大部分問題。

參考

相關文章