Javascript 代碼風(fēng)格

2023-02-17 10:37 更新

我們的代碼必須盡可能的清晰和易讀。

這實際上是一種編程藝術(shù) —— 以一種正確并且人們易讀的方式編碼來完成一個復(fù)雜的任務(wù)。一個良好的代碼風(fēng)格大大有助于實現(xiàn)這一點。

語法

下面是一個備忘單,其中列出了一些建議的規(guī)則(詳情請參閱下文):


現(xiàn)在,讓我們詳細討論一下這些規(guī)則和它們的原因吧。

沒有什么規(guī)則是“必須”的

沒有什么規(guī)則是“刻在石頭上”的。這些是風(fēng)格偏好,而不是宗教教條。

花括號

在大多數(shù)的 JavaScript 項目中,花括號以 “Egyptian” 風(fēng)格(譯注:“egyptian” 風(fēng)格又稱 K&R 風(fēng)格 —— 代碼段的開括號位于一行的末尾,而不是另起一行的風(fēng)格)書寫,左花括號與相應(yīng)的關(guān)鍵詞在同一行上 —— 而不是新起一行。左括號前還應(yīng)該有一個空格,如下所示:

if (condition) {
  // do this
  // ...and that
  // ...and that
}

單行構(gòu)造(如 if (condition) doSomething())也是一個重要的用例。我們是否應(yīng)該使用花括號?如果是,那么在哪里?

下面是這幾種情況的注釋,你可以自己判斷一下它們的可讀性:

  1. 初學(xué)者常這樣寫。非常不好!這里不需要花括號:
  2. if (n < 0) {alert(`Power ${n} is not supported`);}
  3. 拆分為單獨的行,不帶花括號。永遠不要這樣做,添加新行很容易出錯:
  4. if (n < 0)
      alert(`Power ${n} is not supported`);
  5. 寫成一行,不帶花括號 —— 如果短的話,也是可以的:
  6. if (n < 0) alert(`Power ${n} is not supported`);
  7. 最好的方式:
  8. if (n < 0) {
      alert(`Power ${n} is not supported`);
    }

對于很短的代碼,寫成一行是可以接受的:例如 if (cond) return null。但是代碼塊(最后一個示例)通常更具可讀性。

行的長度

沒有人喜歡讀一長串代碼,最好將代碼分割一下。

例如:

// 回勾引號 ` 允許將字符串拆分為多行
let str = `
  ECMA International's TC39 is a group of JavaScript developers,
  implementers, academics, and more, collaborating with the community
  to maintain and evolve the definition of JavaScript.
`;

對于 if 語句:

if (
  id === 123 &&
  moonPhase === 'Waning Gibbous' &&
  zodiacSign === 'Libra'
) {
  letTheSorceryBegin();
}

一行代碼的最大長度應(yīng)該在團隊層面上達成一致。通常是 80 或 120 個字符。

縮進

有兩種類型的縮進:

  • 水平方向上的縮進:2 或 4 個空格。
  • 一個水平縮進通常由 2 或 4 個空格或者 “Tab” 制表符(?Tab ?鍵)構(gòu)成。選擇哪一個方式是一場古老的圣戰(zhàn)。如今空格更普遍一點。

    選擇空格而不是 tabs 的優(yōu)點之一是,這允許你做出比 “Tab” 制表符更加靈活的縮進配置。

    例如,我們可以將參數(shù)與左括號對齊,像下面這樣:

    show(parameters,
         aligned, // 左邊有 5 個空格
         one,
         after,
         another
      ) {
      // ...
    }
  • 垂直方向上的縮進:用于將代碼拆分成邏輯塊的空行。
  • 即使是單個函數(shù)通常也被分割為數(shù)個邏輯塊。在下面的示例中,初始化的變量、主循環(huán)結(jié)構(gòu)和返回值都被垂直分割了:

    function pow(x, n) {
      let result = 1;
      //              <--
      for (let i = 0; i < n; i++) {
        result *= x;
      }
      //              <--
      return result;
    }

    插入一個額外的空行有助于使代碼更具可讀性。寫代碼時,不應(yīng)該出現(xiàn)連續(xù)超過 9 行都沒有被垂直分割的代碼。

分號

每一個語句后面都應(yīng)該有一個分號。即使它可以被跳過。

有一些編程語言的分號確實是可選的,那些語言中也很少使用分號。但是在 JavaScript 中,極少數(shù)情況下,換行符有時不會被解釋為分號,這時代碼就容易出錯。更多內(nèi)容請參閱 代碼結(jié)構(gòu) 一章的內(nèi)容。

如果你是一個有經(jīng)驗的 JavaScript 程序員,你可以選擇像 StandardJS 這樣的無分號的代碼風(fēng)格。否則,最好使用分號以避免可能出現(xiàn)的陷阱。大多數(shù)開發(fā)人員都應(yīng)該使用分號。

嵌套的層級

盡量避免代碼嵌套層級過深。

例如,在循環(huán)中,有時候使用 ?continue ?指令以避免額外的嵌套是一個好主意。

例如,不應(yīng)該像下面這樣添加嵌套的 ?if ?條件:

for (let i = 0; i < 10; i++) {
  if (cond) {
    ... // <- 又一層嵌套
  }
}

我們可以這樣寫:

for (let i = 0; i < 10; i++) {
  if (!cond) continue;
  ...  // <- 沒有額外的嵌套
}

使用 if/else 和 return 也可以做類似的事情。

例如,下面的兩個結(jié)構(gòu)是相同的。

第一個:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
  } else {
    let result = 1;

    for (let i = 0; i < n; i++) {
      result *= x;
    }

    return result;
  }
}

第二個:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
    return;
  }

  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}

但是第二個更具可讀性,因為 n < 0 這個“特殊情況”在一開始就被處理了。一旦條件通過檢查,代碼執(zhí)行就可以進入到“主”代碼流,而不需要額外的嵌套。

函數(shù)位置

如果你正在寫幾個“輔助”函數(shù)和一些使用它們的代碼,那么有三種方式來組織這些函數(shù)。

  1. 在調(diào)用這些函數(shù)的代碼的 上方 聲明這些函數(shù):
  2. // 函數(shù)聲明
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
    
    // 調(diào)用函數(shù)的代碼
    let elem = createElement();
    setHandler(elem);
    walkAround();
  3. 先寫調(diào)用代碼,再寫函數(shù)
  4. // 調(diào)用函數(shù)的代碼
    let elem = createElement();
    setHandler(elem);
    walkAround();
    
    // --- 輔助函數(shù) ---
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
  5. 混合:在第一次使用一個函數(shù)時,對該函數(shù)進行聲明。

大多數(shù)情況下,第二種方式更好。

這是因為閱讀代碼時,我們首先想要知道的是“它做了什么”。如果代碼先行,那么在整個程序的最開始就展示出了這些信息。之后,可能我們就不需要閱讀這些函數(shù)了,尤其是它們的名字清晰地展示出了它們的功能的時候。

風(fēng)格指南

風(fēng)格指南包含了“如何編寫”代碼的通用規(guī)則,例如:使用哪個引號、用多少空格來縮進、一行代碼最大長度等非常多的細節(jié)。

當(dāng)團隊中的所有成員都使用相同的風(fēng)格指南時,代碼看起來將是統(tǒng)一的。無論是團隊中誰寫的,都是一樣的風(fēng)格。

當(dāng)然,一個團隊可以制定他們自己的風(fēng)格指南,但是沒必要這樣做?,F(xiàn)在已經(jīng)有了很多制定好的代碼風(fēng)格指南可供選擇。

一些受歡迎的選擇:

如果你是一個初學(xué)者,你可以從本章中上面的內(nèi)容開始。然后你可以瀏覽其他風(fēng)格指南,并選擇一個你最喜歡的。

自動檢查器

檢查器(Linters)是可以自動檢查代碼樣式,并提出改進建議的工具。

它們的妙處在于進行代碼風(fēng)格檢查時,還可以發(fā)現(xiàn)一些代碼錯誤,例如變量或函數(shù)名中的錯別字。因此,即使你不想堅持某一種特定的代碼風(fēng)格,我也建議你安裝一個檢查器。

下面是一些最出名的代碼檢查工具:

  • JSLint —— 第一批檢查器之一。
  • JSHint —— 比 JSLint 多了更多設(shè)置。
  • ESLint —— 應(yīng)該是最新的一個。

它們都能夠做好代碼檢查。我使用的是 ESLint

大多數(shù)檢查器都可以與編輯器集成在一起:只需在編輯器中啟用插件并配置代碼風(fēng)格即可。

例如,要使用 ESLint 你應(yīng)該這樣做:

  1. 安裝 Node.JS
  2. 使用 ?npm install -g eslint? 命令(npm 是一個 JavaScript 包安裝工具)安裝 ESLint。
  3. 在你的 JavaScript 項目的根目錄(包含該項目的所有文件的那個文件夾)創(chuàng)建一個名為 ?.eslintrc? 的配置文件。
  4. 在集成了 ESLint 的編輯器中安裝/啟用插件。大多數(shù)編輯器都有這個選項。

下面是一個 .eslintrc 文件的例子:

{
  "extends": "eslint:recommended",
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "rules": {
    "no-console": 0,
    "indent": 2
  }
}

這里的 "extends" 指令表示我們是基于 “eslint:recommended” 的設(shè)置項而進行設(shè)置的。之后,我們制定我們自己的規(guī)則。

你也可以從網(wǎng)上下載風(fēng)格規(guī)則集并進行擴展。有關(guān)安裝的更多詳細信息,請參見 https://eslint.org/docs/user-guide/getting-started。

此外,某些 IDE 有內(nèi)建的檢查器,這非常方便,但是不像 ESLint 那樣可自定義。

總結(jié)

本章描述的(和提到的代碼風(fēng)格指南中的)所有語法規(guī)則,都旨在幫助你提高代碼可讀性。它們都是值得商榷的。

當(dāng)我們思考如何寫“更好”的代碼的時候,我們應(yīng)該問自己的問題是:“什么可以讓代碼可讀性更高,更容易被理解?”和“什么可以幫助我們避免錯誤?”這些是我們討論和選擇代碼風(fēng)格時要牢記的主要原則。

閱讀流行的代碼風(fēng)格指南,可以幫助你了解有關(guān)代碼風(fēng)格的變化趨勢和最佳實踐的最新想法。

任務(wù)


不好的風(fēng)格

重要程度: 4

下面的代碼風(fēng)格有什么問題?

function pow(x,n)
{
  let result=1;
  for(let i=0;i<n;i++) {result*=x;}
  return result;
}

let x=prompt("x?",''), n=prompt("n?",'')
if (n<=0)
{
  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
}
else
{
  alert(pow(x,n))
}

修復(fù)它。


解決方案

你可以注意到以下幾點:

function pow(x,n)  // <- 參數(shù)之間沒有空格
{  // <- 花括號獨占了一行
  let result=1;   // <- = 號兩邊沒有空格
  for(let i=0;i<n;i++) {result*=x;}   // <- 沒有空格
  // { ... } 里面的內(nèi)容應(yīng)該在新的一行上
  return result;
}

let x=prompt("x?",''), n=prompt("n?",'') // <-- 從技術(shù)的角度來看是可以的,
// 但是拆分成 2 行會更好,并且這里也缺了空格和分號 ;
if (n<=0)  // <- (n <= 0) 里面沒有空格,并且應(yīng)該在本行上面加一個空行
{   // <- 花括號獨占了一行
  // 下面的一行代碼太長了,可以將其拆分成 2 行以提高可讀性
  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
}
else // <- 可以像 "} else {" 這樣寫在一行上
{
  alert(pow(x,n))  // 缺失了空格和分號 ;
}

修改后的版本:

function pow(x, n) {
  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}

let x = prompt("x?", "");
let n = prompt("n?", "");

if (n <= 0) {
  alert(`Power ${n} is not supported,
    please enter an integer number greater than zero`);
} else {
  alert( pow(x, n) );
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號