詳解JS ES6編碼規范

1、塊級作用域

1.1、let取代var

ES6 提出瞭兩個新的聲明變量的命令: let 和const。其中,let可以完全取代var,因為兩者語義相同,而且let沒有副作用。

var命令存在變量提升的特性,而let沒有這個命令。

所謂變量提升,即指變量可以先使用,再聲明,顯然,這種編碼規范非常不適合閱讀。

1.2、全局常量和線程安全

在let和const之間,優先使用const。

let應出現在單線程模塊代碼內,而const則非常適合多線程。

 // bad
    var a = 1, b = 2, c = 3;

    // good
    const a = 1;
    const b = 2;
    const c = 3;

    // best
    const[a, b, c] = [1, 2, 3];

2、字符串

靜態字符串一律使用單引號或者反引號,不推薦使用雙引號。

動態字符串(字符串模板)使用反引號。

// bad
const a = "zhaoyi";
const b = a + ", hello.";

// little good
const c = `zhaoyi`;

// very good
const a = 'zhaoyi';
const b = `zhaoyi,${a}, I say your name twice`;

3、解構賦值

1、使用數組成員對變量進行賦值時,優先使用解構賦值。

const arr = ['I', 'love', 'you'];

// bad
const one = arr[0];
const two = arr[1];
const three = arr[2];

// good
const [first, second, third] = arr;

// test
console.log(first, second, third);// I love you

2、函數的參數如果是對象的成員,優先使用解構賦值。

// bad
function getUserInfo(user){
    const name = user.name;
    const age = user.age;
}

// good
function getUserInfo2(user){
    const {name, age} = user;
    console.log(name, age); // 
}

// test
getUserInfo2({name: 'zhaoyi', age: 20}); // zhaoyi 20

3、如果函數返回多個值,優先使用對象的結構賦值,而不是數組的結構賦值。這樣便於以後添加返回值,以及更改返回值的順序。

// bad
function getInfo(input){
    return [left, right];
}

// good
function getInfo2(input){
    return {left, right};
}

// 此處使用對象的解構賦值方式接收返回結果
const {left, right} = getInfo2('test');

4、對象

1、單行定義的對象,最後一個成員不以逗號結尾。多行定義的對象,最後一個成員以逗號結尾。

// bad
const a1 = {k1: v1, k2: v2, k3: v3,};

// good 
const a2 = {k1: v1, k2: v2, k3: v3};

// bad
const b1 = {
    k1: v1,
    k2: v2
};

// good
const b2 = {
    k1: v1,
    k2: v2,
};

2、對象盡量靜態化,一旦定義,就不得隨意添加新的屬性。如果添加屬性不可避免,要使用assign方法。

// bad
const a = {};
a.attr = 3;

// if reshape anavoidable(若無可避免)
const b = {};
Object.assign(b, {atrr: 3});

// good
const c = {attr: null};
c.attr = 3;

// test
console.log(a); //{attr: 3}
console.log(b); //{attr: 3}
console.log(c); //{attr: 3}

3、如果對象的屬性名是動態的(所謂動態是指,需要通過計算得到),可以在創建對象的時候使用屬性表達式定義。(此種情況在開發時,並不多見。)

5、數組

使用擴展運算符(…)復制數組。

// bad
function copyArr1(arr){
    const itemCopy = [];
    for (let index = 0; index < arr.length; index++) {
        itemCopy[index] = arr[index];
    }
    return itemCopy;
}

// good
function copyArr2(arr){
    return [...arr];
}

// test
const arr = ['z', 'y', 'z'];
console.log(copyArr1(arr)); // ["z", "y", "z"]
console.log(copyArr2(arr)); // ["z", "y", "z"]

使用Array.from 方法將類似數組的對象轉為數組。

const obj = { "0": "a", "1": "b", length: 2};
const arr = Array.from(obj);

// test
console.log(arr); //  ["a", "b"]

6、函數

1、立即執行函數可以寫成箭頭函數的形式。

(() => {
    console.log('this is a good night.');
})();

2、在需要使用函數表達式的場合,盡量用箭頭函數代替。因為這樣可以更簡潔,而且綁定瞭this。

// bad
const sayHello = ['a', 'b', 'c'].map(function (w){
    return 'Hello, ' + w;
})

// good 
const sayHello2 = ['a', 'b', 'c'].map(w => {
    return 'Hello, ' + w;
});

// test
console.log(sayHello2); //  ["Hello, a", "Hello, b", "Hello, c"]  

3、箭頭函數取代Function.prototype.bind,不應再用self/_this/that綁定this.

// bad
const self = this;
const boundMethod = function(...params){
    return method.apply(self, params);
}

// acceptable
const boundMethod2 = method.bind(this);

// best
const boundMehod3 = (...params) => method.apply(this, params);

4、單行簡單、無需復用的函數,建議采用箭頭函數。如果函數體較為復雜,行數較多,還是應采用傳統的函數寫法。

5、所有配置項都應該集中在一個對象,放在到最後一個參數,佈爾值不可以直接作為參數。

// bad
function divide(a, b, option = false){

}

// good
function divide(a, b, {option = false} = {}){

}

6、不要在函數體內使用arguments變量,使用rest運算符(…)代替。因為rest運算符可以顯示聲明我們想要獲取的參數,而且arguments是一個類似數組的對象,而rest元素安撫可以提供一個真正的數組。

// bad
function f1(){
    const args = Array.prototype.slice.call(arguments);
    return args.join('-');
}

// good
function f2(...args){
    return args.join('-');
}

// test
console.log(f1(1, 2, 3)); // 1-2-3
console.log(f2(1, 2, 3)); // 1-2-3

擴展運算符用三個點號表示,功能是把數組或類數組對象展開成一系列用逗號隔開的值;而rest運算符也是三個點號,不過其功能與擴展運算符恰好相反,把逗號隔開的值序列組合成一個數組。rest是剩餘的意思。

7、使用默認值語法設置函數參數的默認值。

// bad
function handleThings(opts){
    opts = opts || {};
    // ...
}

// good
function handleThings2(opts = {}){
    // ...
}

7、Map結構

Map和Object給人的感覺是同一個數據類型,但是在實際語義還需要更為準確的區分,原則如下:

  • 模擬實體對象時,使用Object;
  • 隻需要k-v鍵值對數據結構時,使用Map;

Map擁有內建的遍歷機制(實現瞭Iterator結構)

// Map擁有許多初始化方式,這裡使用數組成員為兩個長度的數組進行初始化(第一個元素為K,第二個元素為V)
let map = new Map([['k1', 'I'], ['k2', 'love'], ['k3', 'your']]);

// 遍歷K
for(const key of map.keys()){
    console.log(key);
    // k1
    // k2
    // k3
}

// 遍歷V
for (const value of map.values()) {
    console.log(value);
    // I
    // love
    // you
}

// 遍歷K-V
for (const item of map.entries()) {
    console.log(item);
    // ['k1', 'I']
    // ['k2', 'love']
    // ['k3', 'your']
}

8、Class

1、總是用Class取代需要prototype的操作。因為Class的寫法更簡潔,更易於理解。接觸過Java、C#比較多的朋友想必更喜歡這樣的類語法方式。

// bad
function Queue1(contents = []){
    this._queue = [...contents];
}
Queue1.prototype.pop = function(){
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
}

// good
class Queue {
    constructor(contents = []){
        // 這裡為什麼不用this._queue = contents;呢?
        // 讀過effective java的朋友想必知道一個規則:
        // 那就是在設計構造函數時,若傳入的參數中有可變類型(對象、數組),
        // 則構造函數內部接收此參數時應使用這個對象的拷貝。
        // 這樣可以避免外部參數對象的更改影響到類本身的實例。
        // 因此,此處的contents需要拷貝一個復制在進行賦值。
        this._queue = [...contents];
    }
    pop() {
        const value = this._queue[0];
        this._queue.splice(0, 1);
        return value;
    }
}

// test
q = new Queue([1, 2, 3]);

console.log(q.pop()); // 1
console.log(q.pop()); // 2
console.log(q.pop()); // 3
console.log(q.pop()); // undefined

2、使用extends實現繼承,因為這樣可以更簡單,不存在破壞instanceof運算的危險。

// Queue為上一個例子的類
class PeekQueue extends Queue{
    // ...
}

9、模塊

1、Module語法是js模塊的標準寫法,要堅持使用這種寫法。使用import取代require。

// bad
const ma = require('moduleA');
const f1 = ma.f1;
const f2 = ma.f2;

// good
import {f1, f2} from 'moduleA';

2、使用export取代module.export

// bad
module.exports = SomeObj;

// good
export default SomeObj; 

3、如果模塊隻有一個輸出值,就使用 export default; 若有鍍鉻,就不要使用 export default, 不要同時使用 export default 和 普通的 export,雖然規則上允許此種編寫代碼的方式。

4、不要在模塊中使用通配符,因為這樣可以確保模塊中有一個默認輸出:export default。

// bad
import * as myObject './someModule';

// good
import myObject from './someModule';

5、如果模塊默認輸出一個函數,函數的首字母應該小寫。

 function someFunction(){
     // ...
 }
 export default someFunction;

6、 如果模塊默認輸出一個對象,對象名的首字母應該大寫。

const someObj = {
    // ...
}
export default SomeObj;

10、ESLint

前面說瞭那麼多規則,其實隻是規則范本的冰山一角,真正想要寫出格式優美、符合主流廠商規范的代碼,僅僅靠我們的自覺是不夠的。

有沒有什麼類似軟件編譯工具檢查代碼正確性來檢查代碼編寫規范的軟件呢,答案是有的。

ESLint就是這樣的一款檢查工具。可以用於保證寫出語法正確、風格統一的代碼。

以下是安裝ESLink的教程(確保您的環境已經安裝瞭npm),當然,如果您使用一些腳手架工具(例如@vue-cli)等方式生成的項目,那麼這樣的項目都是提供瞭可選的eslint插件的。當前版本為: v6.6.0。該版本的eslint提供瞭更為簡單的配置方式,可以參考https://eslint.bootcss.com/docs/user-guide/getting-started/進行配置。以下是一個粗略的配置步驟

1、安裝所需插件

$ npm install eslint -g

2、生成package.json文件

$ npm init

該方法會在當前目錄生成package.json文件,該文件類似於環境的說明文件。

3、生成eslint配置文件

$ eslint --init

該命令會詢問你使用哪種類型的配置(通過上下箭頭選取)

  • 推薦選用json或者JavaScript類型,我這裡使用的是JavaScript類型的配置文件
  • style guide選用airbnb。

其他的選項根據你的需要進行選取即可。完成選擇之後,會自動下載所需要的依賴包。

生成的配置文件內容大致如下:

module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'airbnb-base',
  ],
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  rules: {
  },
};

我們在該配置文件中可以修改驗證規則,具體的內容同樣參考上面給出的鏈接。

4、在當前目錄下,創建一個js文件

// index.js
var unused = '灰與幻想的格林姆迦爾';

function hello(){
    var message = "Hello, zhaoyi!";
    alert(message);
}
  
hello(); 

5、通過eslint驗證代碼編寫正確性

$ eslint index.js

 1:12  error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  2:1   error    Unexpected var, use let or const instead         no-var

  2:5   error    ‘unused’ is assigned a value but never used      no-unused-vars

  2:27  error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  3:1   error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  4:17  error    Missing space before opening brace               space-before-blocks

  4:18  error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  5:1   error    Expected indentation of 2 spaces but found 4     indent

  5:5   error    Unexpected var, use let or const instead         no-var

  5:19  error    Strings must use singlequote                     quotes

  5:36  error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  6:1   error    Expected indentation of 2 spaces but found 4     indent

  6:5   warning  Unexpected alert                                 no-alert

  6:20  error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  7:2   error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  8:1   error    Trailing spaces not allowed                      no-trailing-spaces

  8:3   error    Expected linebreaks to be ‘LF’ but found ‘CRLF’  linebreak-style

  9:9   error    Trailing spaces not allowed                      no-trailing-spaces

  9:10  error    Newline required at end of file but not found    eol-last

其中,有一種錯誤其實是因為git文件格式轉化的問題:

… linebreak-style

我們可以在配置文件中移除該檢測:在rules下添加’linebreak-style’: [0, ‘error’, ‘windows’].

rules: {
    'linebreak-style': [0, 'error', 'windows']
  }

繼續運行檢測命令,可以看到如下的輸出:

2:1   error    Unexpected var, use let or const instead      no-var

2:5   error    ‘unused’ is assigned a value but never used   no-unused-vars

5:1   error    Expected indentation of 2 spaces but found 4  indent

5:5   error    Unexpected var, use let or const instead      no-var

5:19  error    Strings must use singlequote                  quotes

6:1   error    Expected indentation of 2 spaces but found 4  indent

6:5   warning  Unexpected alert                              no-alert

8:1   error    Trailing spaces not allowed                   no-trailing-spaces

9:9   error    Trailing spaces not allowed                   no-trailing-spaces

可以看到,我們許多不規范的操作都會警告瞭。比如縮進不要用四空格(其實是我們的編譯器自帶,而且我們習慣瞭的),不要加多餘的空格,以及刪掉沒有使用過的聲明變量,不推薦使用var類型等等。

如果覺得合情合理,那麼遵循之;如果覺得縮進這些不符合自己的習慣,也可以通過配置文件進行關閉/修改等操作達到預期的效果。

以上就是詳解JS ES6編碼規范的詳細內容,更多關於JS ES6編碼規范的資料請關註WalkonNet其它相關文章!

推薦閱讀: