前端開(kāi)發(fā)規(guī)范

參考airbnb規(guī)范,該規(guī)范是airbnb定義的eslint規(guī)范,從github上來(lái)看,受歡迎程度很高,選擇這個(gè)做為規(guī)范標(biāo)準(zhǔn)

ESLint基礎(chǔ)知識(shí)

安裝

npm install --save-dev eslint

設(shè)置基本配置(生成.eslintrc)

./node_modules/.bin/eslint --init

運(yùn)行相關(guān)檢查

./node_modules/.bin/eslint 待檢查js文件

parserOptions檢驗(yàn)ecma版本,以及ecma的一些特性
{
 "parserOptions": {
  "ecmaVersion": 6, //指定ECMAScript支持的版本,6為ES6
  "sourceType": "module", //指定來(lái)源的類型,有兩種”script”或”module”
  "ecmaFeatures": {
   "jsx": true//啟動(dòng)JSX
  },
 }
}
Parser設(shè)定對(duì)腳步的解析,默認(rèn)是使用esprima,還可以設(shè)置為babel-eslint(目前項(xiàng)目都會(huì)使用es6的一些語(yǔ)法,所以這個(gè)參數(shù)一般都要設(shè)為babel-eslint)

對(duì)腳步解析是指把源碼轉(zhuǎn)成可AST(Abstract Syntax Tree抽象語(yǔ)法樹(shù),是源代碼語(yǔ)法所對(duì)應(yīng)的樹(shù)狀結(jié)構(gòu))

"parser": "babel-eslint" // 要先安裝babel-eslint
Environments指定腳本的運(yùn)行環(huán)境,不同的環(huán)境會(huì)有不同的預(yù)定義全局變量。
"env": {
    "browser": true,
    "commonjs": true,
    "es6": true
}, // 可設(shè)定參數(shù)很多,具體可以參考官方文檔
extends設(shè)置繼承的規(guī)則集,可以是單個(gè)字符也可以是多個(gè)配置的數(shù)組
"extends": "eslint:recommended", // 啟用核心配置規(guī)則

除了可以使用核心配置外,還可以使用可別人共享的配置,extends屬性值可以省略包名的前綴 eslint-config-,比如使用eslint-config-standard

使用這個(gè)規(guī)則時(shí),需要先安裝

npm install --save-dev eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node

在相關(guān)配置中使用這個(gè)規(guī)則

"extends": "standard"

另外雖然可以自己設(shè)定相關(guān)配置,然后共享出來(lái),這里直接選用airbnb的標(biāo)準(zhǔn)進(jìn)行代碼檢查

eslint-config-airbnb的標(biāo)準(zhǔn)需要eslint-plugin-jsx-a11y、eslint-plugin-react、eslint-plugin-import依賴,安裝時(shí)要一并安裝

npm install --save-dev eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-import eslint-plugin-react

"extends": "airbnb"
globals 設(shè)置可以是全局變量的變量("extends": "eslint:recommended"會(huì)啟用no-undef禁用未聲明的變量)
// var1和var2是全局變量,var1可以被重寫,var2不可以被重寫
{
    "globals": {
        "var1": true,
        "var2": false
    },
}
eslint rules規(guī)則
rules: {
    rules name: [錯(cuò)誤級(jí)別, 相應(yīng)規(guī)則]
}

rules name:就是對(duì)應(yīng)的限制規(guī)則

錯(cuò)誤級(jí)別:

  • "off"/0 關(guān)閉規(guī)則
  • "warn"/1 警告
  • "error"/2 錯(cuò)誤
"rules": {
    "indent": ["error", 4] // 限制使用4個(gè)空格
}
plugins eslint允許使用的第三方插件,在使用第三方插件時(shí),如果需要設(shè)置相關(guān)規(guī)則可以使用插件名/規(guī)則ID的形式添加
{
    "plugins": [
        "plugin1"
    ],
    "rules": {
        "eqeqeq": "off",
        "curly": "error",
        "quotes": ["error", "double"],
        "plugin1/rule1": "error"
    }
}
部分代碼過(guò)濾,不進(jìn)行相關(guān)檢查
// 使用方式如下,如果下面的代碼不加eslint相關(guān)過(guò)濾信息,在進(jìn)行eslint檢查時(shí)會(huì)報(bào)no-console的錯(cuò)誤,加上后不再進(jìn)行相關(guān)檢查,這個(gè)方式最好不要使用,除非是在開(kāi)發(fā)過(guò)程中臨時(shí)調(diào)試代碼時(shí)使用
var add = function () {
    /* eslint-disable */
    console.log('add');
    /* eslint-enable */
};
// 和注釋一樣,也可以使用//來(lái)過(guò)濾
alert('foo'); // eslint-disable-line
設(shè)置使用eslint時(shí)的檢查范圍

使用eslint檢查代碼時(shí),ESLint將自動(dòng)在要檢測(cè)的文件目錄里尋找配置文件,然后緊接著是父級(jí)目錄,一直到文件系統(tǒng)的根目錄(當(dāng)有多個(gè)檢查文件時(shí),離檢測(cè)的文件最近的.eslintrc文件有最高優(yōu)先級(jí)),在項(xiàng)目開(kāi)發(fā)時(shí),我們一般會(huì)希望檢查規(guī)則一致,所以通常只會(huì)在項(xiàng)目的根目錄設(shè)置一個(gè).eslintrc,避免在其他子目錄下添加這個(gè)配置文件,如果想要eslint只檢查當(dāng)前目錄忽略其他目錄,可以在配置中添加root: true,把相關(guān)檢測(cè)限制到一個(gè)特定的項(xiàng)目

home
└── user
    ├── .eslintrc <- 根目錄配置
    └── projectA
        ├── .eslintrc  <- 項(xiàng)目配置
        └── lib
            ├── .eslintrc  <- 庫(kù)文件配置
            └── main.js
// 如果在lib下的配置文件中設(shè)置了root:true,在對(duì)main.js文件檢查時(shí)會(huì)忽略項(xiàng)目配置            

如果同一目錄下 .eslintrc 和 package.json 同時(shí)存在,.eslintrc 優(yōu)先級(jí)高會(huì)被使用,package.json 文件將不會(huì)被使用,不要在package.json的文件中添加檢測(cè)規(guī)則文件

設(shè)置eslint檢查的忽略文件

在項(xiàng)目根目錄創(chuàng)建一個(gè).eslintignore文件,用來(lái)告訴ESLint去忽略特定的文件和目錄,設(shè)定規(guī)則如下:

  • 以#開(kāi)頭的被當(dāng)作注釋,不會(huì)影響忽略
  • 路徑是相對(duì)于.eslintignore的位置
  • 忽略模式同.gitignore規(guī)范
  • 以!開(kāi)頭是否定模式,它將會(huì)重新包含一個(gè)之前
// 一個(gè)模擬的例子,下面例子表示在lib目錄下所有目錄下的js都不進(jìn)行代碼檢查
src/assets/lib/**/*.js

airbnb規(guī)范標(biāo)準(zhǔn)-js部分

標(biāo)準(zhǔn)中分成建議使用和校驗(yàn)(下面使用[0]代碼建議使用,[1]代表校驗(yàn)),建議使用并不會(huì)報(bào)錯(cuò),校驗(yàn)會(huì)報(bào)錯(cuò)(判斷依據(jù)是我使用airbnb官方實(shí)例,在配置好的環(huán)境中運(yùn)行,看是否會(huì)報(bào)錯(cuò))

引用

  • 對(duì)變量的聲明不要使用var,如果是聲明后不再重現(xiàn)分配的變量要使用const,如果是重現(xiàn)分配的變量要使用let [1]
  • 注意let和const的塊級(jí)作用域 [0]

對(duì)象

  • 使用對(duì)象直接量創(chuàng)建對(duì)象 [1]
// bad
const item = new Object();

// good
const item = {};
  • 如果一個(gè)對(duì)象的key值包含動(dòng)態(tài)屬性,要保證對(duì)象的所有屬性都是在一個(gè)地方進(jìn)行定義 [0]
function getKey(k) {
  return `a key named ${k}`;
}

// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};
// 從上面的代碼我們可以看出,這個(gè)建議的是要我們把與對(duì)象相關(guān)的屬性,都在一個(gè)地方定義,便于維護(hù)
  • 在定義方法時(shí),使用簡(jiǎn)寫的方式進(jìn)行定義 [1]
// bad
const atom = {
  value: 1,
  addValue: function (value) {
    return atom.value + value;
  },
};

// good
const atom = {
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};
  • 在定義對(duì)象屬性時(shí),如果key值和value值同名,要使用簡(jiǎn)寫形式 [1]
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
  lukeSkywalker,
};
  • 在定義對(duì)象時(shí),簡(jiǎn)寫的屬性和非簡(jiǎn)寫的屬性要按順序?qū)懀灰熘鴮?[0]
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
};

// good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
};
  • 在定義對(duì)象時(shí),key值只有在無(wú)效識(shí)別時(shí)添加單引號(hào) [1]
// bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
};

// good
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5,
};
  • 不允許直接使用Object.prototype的相關(guān)方法,比如hasOwnProperty, propertyIsEnumerable, 和 isPrototypeOf.因?yàn)樵诙x對(duì)象時(shí),有可能會(huì)覆蓋了這些方法 [1]
const item = {
    name: 'rede',
};
// bad
console.log(item.hasOwnProperty('name'));
// good
console.log(Object.prototype.hasOwnProperty.call(item, 'name'));
// best
const has = Object.prototype.hasOwnProperty;
console.log(has.call(item, 'name'));
  • 使用對(duì)象展開(kāi)符淺復(fù)制對(duì)象,不要使用Object.assign [0]
// very bad
// 本意是復(fù)制一個(gè)對(duì)象,在從復(fù)制出的對(duì)象上刪除某個(gè)值,但實(shí)際上原始對(duì)象的值也會(huì)被影響
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 });
delete copy.a;

// bad
// 可以達(dá)到預(yù)期目的,但在寫法上可讀性不好
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
delete copy.a;

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

數(shù)組

  • 在定義數(shù)組時(shí),使用數(shù)組直接量,不要使用new Array的形式 [1]
// bad
const items = new Array();

// good
const items = [];
  • 在向數(shù)組添加元素時(shí),不要直接賦值,通過(guò)push添加(注意指的是添加,并不包括修改的情況) [0]
const items = [];
// bad
items[items.length] = 'test';
// good
items.push('test');
  • 復(fù)制數(shù)組時(shí),使用展開(kāi)操作符 [0]
const items = [1, 2, 4, 8, 16];
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i += 1) {
  itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
  • 在把類數(shù)組對(duì)象轉(zhuǎn)為數(shù)組對(duì)象時(shí),使用展開(kāi)操作符來(lái)替代Array.from [0]
const foo = document.querySelectorAll('.foo');
 
// good
const nodes = Array.from(foo);
 
// best
const nodes = [...foo];
  • 使用Array.from代替展開(kāi)操作符來(lái)處理針對(duì)數(shù)組的映射操作,可以避免操作創(chuàng)建中間數(shù)組 [0]
const foo = [1, 2, 3];

// bad
const baz = [...foo].map(x => x + x);

// good
const baz = Array.from(foo, x => x + x);
  • 數(shù)組的某些方法會(huì)存在回調(diào)函數(shù),eslint會(huì)強(qiáng)制回調(diào)函數(shù)使用return,不過(guò)airbnb的規(guī)范中認(rèn)為,如果某些數(shù)組的回調(diào)只是單條語(yǔ)句的那種,是可以省略return的 [0]

主要是一些可以對(duì)數(shù)組進(jìn)行迭代的方法,比如every,filter,find,findIndex,map,reduce,some,sort

// airbnb同樣標(biāo)記這種寫法為good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

// 下面的寫法也是good,兩種寫法都沒(méi)有問(wèn)題,不過(guò)可能下面這種更易讀些
[1, 2, 3].map(x => (x + 1)*x)

// airbnb下面標(biāo)準(zhǔn)的是bad,剛開(kāi)始我也以為是針對(duì)這個(gè)規(guī)則的說(shuō)法
// 實(shí)際運(yùn)行下,發(fā)現(xiàn)針對(duì)這種寫法報(bào)的錯(cuò)誤是no-else-return,就是不允許在return之后使用else,哪怕看起來(lái)是另外的邏輯分支
inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  } else {
    return false;
  }
});

// 后來(lái)嘗試了下,結(jié)合eslint頁(yè)面中的規(guī)則,估計(jì)這個(gè)地方的代碼應(yīng)該是這樣
// 再次運(yùn)行監(jiān)測(cè),會(huì)報(bào)幾個(gè)錯(cuò)誤,包括提到的array-callback-return,就是這里必須設(shè)定return的值
const inbox = ['black', 'white', 'yellow'];
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
    } else {
        return;
    }
});

// good
const inbox = ['black', 'white', 'yellow'];
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
        return author === 'Harper Lee';
    }
    return false;
});
  • 如果數(shù)組有多行,在數(shù)組的中括號(hào)前后要進(jìn)行換行 [0]
// bad
const arr = [
  [0, 1], [2, 3], [4, 5],
];
const numberInArray = [
    1, 2,
];
// good
const arr = [[0, 1], [2, 3], [4, 5]];
const numberInArray = [
    1, 
    2,
];

解構(gòu)

  • 優(yōu)先使用對(duì)象解構(gòu)來(lái)訪問(wèn)對(duì)象屬性 [1]
// bad
function getFullName(user) {
    const firstName = user.firstName;
    const lastName = user.lastName;
    return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
    const { firstName, lastName } = user;
    return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}
  • 優(yōu)先使用數(shù)組解構(gòu)從數(shù)組索引創(chuàng)建變量 [0]
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 用對(duì)象解構(gòu)去處理多個(gè)返回值 [1]
// 
const input = {
    left: '0px', right: '0px', top: '0px', bottom: '0px',
};
// bad
function processInput(input) {
    const left = input.left;
    const right = input.right;
    const top = input.top;
    const bottom = input.bottom;
    return [left, right, top, bottom];
}
const [left, __, top] = processInput(input);

// good
function processInput({
    left, right, top, bottom,
}) {
    return {
        left, right, top, bottom,
    };
}
const { left, top } = processInput(input);

字符串

  • 字符串使用單引號(hào) [1]
// bad
const name = "Capt. Janeway";
// bad
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';
  • 當(dāng)字符串過(guò)長(zhǎng)時(shí),不要使用字符串連接符寫成多行 [1][0]
// bad
// 此時(shí)報(bào)的檢查錯(cuò)誤是no-multi-str,保證字符串不分兩行書寫
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// bad
// 此時(shí)運(yùn)行代碼檢查并不會(huì)報(bào)錯(cuò),針對(duì)這種寫法應(yīng)該是建議不要這么寫
const errorMessage = 'This is a super long error that was thrown because ' +
  'of Batman. When you stop to think about how Batman had anything to do ' +
  'with this, you would get nowhere fast.';
    
// good
// 要是感覺(jué)代碼過(guò)長(zhǎng)不方便看時(shí),可以在編譯器上做相關(guān)設(shè)置,設(shè)置自動(dòng)換行
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
  • 當(dāng)字符串和變量要進(jìn)行連接時(shí),要使用模版字面量 [1]
// bad
function sayHi(name) {
    return 'How are you, ' + name + '?';
}

// bad
// 檢查的錯(cuò)誤template-curly-spacing,禁止模版字符串前后使用空格
function sayHi(name) {
  return `How are you, ${ name }?`;
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}
  • 針對(duì)字符串不要使用eval [1]

  • 在字符串中只在有意義的地方使用轉(zhuǎn)義字符 [1]

// bad
const foo = '\'this\' is \"quoted\"';
// good
const foo = '\'this\' is "quoted"';

函數(shù)

  • 在定義函數(shù)時(shí),使用指定名詞的函數(shù)表達(dá)式,而不是匿名表達(dá)式 [0]

關(guān)于這點(diǎn)建議,最開(kāi)始其實(shí)并不能理解,因?yàn)樽约浩綍r(shí)使用最多的也就是第二種方式,后來(lái)參考github這片文章的討論上的討論,感覺(jué)這么定義還是有必要的,針對(duì)這個(gè)我也單獨(dú)寫了一篇總結(jié),有興趣可以去看看

// bad 
function add (a, b) {
    return a + b;
}

// bad 
const add = function (a, b) {
    return a + b;
}

// good 
const add = function myAdd (a, b) {
    return a + b;
}
  • 立即執(zhí)行函數(shù)要用大括號(hào)包裹起來(lái) [1]
// bad
const x = function () { return { y: 1 };}()
...
// not bad
// 隨著現(xiàn)在模塊的普及立即執(zhí)行函數(shù)使用的應(yīng)該不多了
const x = (function () { return { y: 1 }; }());
  • 禁止循環(huán)中出現(xiàn)函數(shù),如果需要在循環(huán)中定義函數(shù),最好把相關(guān)函數(shù)使用變量定義好再使用,避免形成閉包 [0]

此處例子是我自己添加非airbnb例子

// bad
// 下面循環(huán)中如果把i的定義換成var,那就會(huì)形成一個(gè)最經(jīng)典的閉包問(wèn)題,數(shù)組funcs所有的返回值都是10,使用let會(huì)避免這個(gè)問(wèn)題
const funcs = [];
for (let i = 0; i < 10; i + 1) {
    funcs[i] = function () {
        return i;
    };
}
...
// 如果使用babel轉(zhuǎn)上面的代碼
// 在這里可以看出babel進(jìn)行轉(zhuǎn)換時(shí)會(huì)按這規(guī)則,把本來(lái)定義的函數(shù)提取出來(lái)單獨(dú)定義成一個(gè)變量,再進(jìn)行使用
"use strict";
var funcs = [];
var _loop = function _loop(i) {
    funcs[i] = function () {
        return i;
    };
};
for (var i = 0; i < 10; i + 1) {
    _loop(i);
}

// not bad
const funcs = [];
const printI = function printI(i) {
    return i;
};
for (let i = 0; i < 10; i + 1) {
    funcs[i] = printI(i);
}
...
// babel轉(zhuǎn)碼后為
// 轉(zhuǎn)碼前后其實(shí)變化不大
"use strict";
var funcs = [];
var printI = function printI(i) {
    return i;
};
for (var i = 0; i < 10; i + 1) {
    funcs[i] = printI(i);
}
  • 禁止在代碼塊中使用函數(shù)聲明,不過(guò)可以使用函數(shù)表達(dá)式 [1]
// bad
const currentUser = true;
if (currentUser) {
    function test() {
        console.log('Nope.');
    }
}

// good 
const currentUser = true;
let test;
if (currentUser) {
    test = () => {
        console.log('Nope.');
    };
}
  • 函數(shù)接收的形參不可以使用argumenst [1]
// bad 
function foo(name, options, arguments) {
  // ...
}
  • 函數(shù)內(nèi)部不要使用使用arguments來(lái)替代獲取形參,可以使用rest參數(shù)獲取多余參數(shù) [1]
//bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}
// good
function concatenateAll(...args) {
    return args.join('');
}
  • 使用默認(rèn)參數(shù)語(yǔ)法,不要去使用存在變化的函數(shù)參數(shù) [1]
// bad
// 這個(gè)會(huì)報(bào)no-param-reassign,禁止對(duì) function 的參數(shù)進(jìn)行重新賦值
function handleThings(opts) {
    opts = opts || {};
    ...
}
// bad
// 報(bào)的錯(cuò)誤同上
function handleThings(opts) {
    if (opts === 0) {
        opts = {};
    }
}
// good
function handleThings(opts = {}) {
    // ...
}
  • 避免對(duì)函數(shù)默認(rèn)參數(shù)進(jìn)行不可預(yù)期的操作 [1]
// bad
// 檢查時(shí)會(huì)報(bào)no-plusplus,禁用++
// 這個(gè)例子應(yīng)該是要說(shuō)明,不對(duì)默認(rèn)參數(shù)設(shè)置不確定的因素,就比如這里的b值,兩次調(diào)用返回值都不同,會(huì)令人費(fèi)解
let b = 1;
function count(a = b++) {
    console.log(a);
}
count(); // 1
count(); // 2
  • 始終將默認(rèn)參數(shù),放到最后 [0]
// bad
function handleThings(opts = {}, name) {
    return { opts, name };
}
// good
function handleThings(name, opts = {}) {
    return { opts, name };
}
  • 禁用Function構(gòu)造函數(shù)創(chuàng)建函數(shù) [1]
// bad
const x = new Function('a', 'b', 'return a + b');
// good
const x = function backAdd(a, b) {
    return a + b;
};
  • 強(qiáng)制在函數(shù)圓括號(hào)前和代碼塊之前使用統(tǒng)一的空格 [1]
// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const f = function () {};
const g = function () {};
const h = function () {};
  • 禁止對(duì)函數(shù)參數(shù)再賦值 [1]
// bad
// 怎么都想不到為什么要對(duì)參數(shù)進(jìn)行賦值,報(bào)錯(cuò)為no-param-reassign
function foo(bar) {
    bar = 13;
}
...
// bad
function foo(bar) {
    bar.name = 'foo';
}
  • 在函數(shù)括號(hào)內(nèi)使用一致的換行,如果是多行會(huì)要求最后一項(xiàng)帶逗號(hào) [1]
// normal
// 不使用換行
function foo(bar, baz, quux) {
// ...
}
// bad
// 會(huì)報(bào)function-paren-newline要求使用一致的換行
function foo(bar, 
    baz, 
    quux) {
// ...
}
// good
// 最后一行添加了逗號(hào)
function foo(
    bar,
    baz,
    quux,
) {
// ...
}

箭頭函數(shù)

  • 當(dāng)必須使用匿名函數(shù)時(shí)(比如回調(diào)函數(shù)),可以使用箭頭函數(shù) [1]
// bad
[1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;
}); 
// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});
  • 如果回調(diào)函數(shù)只是簡(jiǎn)單的單行語(yǔ)句,可以省略return [0]
// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

// good,提高可讀性
[1, 2, 3].map(x => (x + 1) * x)
  • 如果表達(dá)式垮多行,將其包裹在括號(hào)中,可以提高代碼的可讀性 [0]
// not good
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod,
));
// good
['get', 'post', 'put'].map(httpMethod => (
    Object.prototype.hasOwnProperty.call(
        httpMagicObjectWithAVeryLongName,
        httpMethod,
    )
));
  • 箭頭函數(shù)體只有一個(gè)參數(shù)時(shí)且不使用大括號(hào),可以省略圓括號(hào)。其它任何情況,參數(shù)都應(yīng)被圓括號(hào)括起來(lái) [0]
// bad
// arrow-parens
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);

// bad
// 因?yàn)榇藭r(shí)使用了大括號(hào),箭頭函數(shù)后面跟隨了代碼塊
[1, 2, 3].map(x => {
    const y = x + 1;
    return x * y;
});
  • 禁止在可能與比較操作符相混淆的地方使用箭頭函數(shù) [1]
// bad
// 此時(shí)的箭頭符號(hào)和比較符號(hào)看起來(lái)很像
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;

// good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);

// good
const itemHeight = (item) => {
    const { height, largeSize, smallSize } = item;
    return height > 256 ? largeSize : smallSize;
};

類和構(gòu)造函數(shù)

  • 總是使用class,避免使用prototype [0]
// bad
function Queue(contents = []) {
    this.queue = [...contents];
}
Queue.prototype.pop = function () {
    const value = this.queue[0];
    this.queue.splice(0, 1);
    return value;
};
// good
class Queue {
    constructor(contents = []) {
        this.queue = [...contents];
    }
    pop() {
        const value = this.queue[0];
        this.queue.splice(0, 1);
        return value;
    }
}
  • 對(duì)類的繼承使用extends [1]
// good
// 這種寫法簡(jiǎn)單明了
class student {
    constructor({ id } = {}) {
        this.name = `stu-${id}`;
    }
    backName() {
        return this.name;
    }
}
class classmates extends student {
    constructor({ id } = {}) {
        super({ id });
        this.id = id;
    }
    id() {
        return this.id;
    }
}
  • 方法可以返回this來(lái)進(jìn)行鏈?zhǔn)秸{(diào)用 [0]
// normal
class student {
    constructor({ id } = {}) {
        this.name = `stu-${id}`;
    }
    setAge({ age } = {}) {
        this.age = age;
    }
    setGender({ gender } = {}) {
        this.gender = gender;
    }
    backName() {
        return this.name;
    }
    backInfo() {
        return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
    }
}
const stu = new student({id: 2333});
stu.setAge({age: 18});
stu.setGender({gender: 'man'});
stu.backInfo();

// good
class student {
    constructor({ id } = {}) {
        this.name = `stu-${id}`;
    }
    setAge({ age } = {}) {
        this.age = age;
        return this;
    }
    setGender({ gender } = {}) {
        this.gender = gender;
        return this;
    }
    backName() {
        return this.name;
    }
    backInfo() {
        return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
    }
}
const stu = new student({id: 2333});
stu.setAge({age: 18}).setGender({gender: 'man'});
stu.backInfo();
  • 定義類時(shí)可以定義一個(gè)toString方法,只要保證該方法不會(huì)產(chǎn)生意外的副作用 [0]
class student {
    constructor({ id } = {}) {
        this.name = `stu-${id}`;
    }
    setAge({ age } = {}) {
        this.age = age;
        return this;
    }
    setGender({ gender } = {}) {
        this.gender = gender;
        return this;
    }
    backName() {
        return this.name;
    }
    backInfo() {
        return `name: ${this.name}, age: ${this.age}, gender: ${this.gender}`;
    }
    toString() {
        return this.backName();
    }
}
  • 如果構(gòu)造函數(shù)只是一個(gè)空的構(gòu)造函數(shù)或只是簡(jiǎn)單的調(diào)用父類,此時(shí)構(gòu)造函數(shù)可以省略 [1]
// bad
// 因?yàn)榇藭r(shí)構(gòu)造函數(shù)constructor沒(méi)有任何作用,此時(shí)沒(méi)必要設(shè)置
class Jedi {
    constructor() {}

    getName() {
        return this.name;
    }
}
// bad
// 此時(shí)只是透?jìng)鲾?shù)據(jù),沒(méi)必要使用構(gòu)造函數(shù)
class Rey extends Jedi {
    constructor(...args) {
        super(...args);
    }
}
// godd
class Rey extends Jedi {
    constructor(...args) {
        super(...args);
        this.name = 'Rey';
    }
}
  • 類的成員禁止重名 [1]
// bad
// 很明顯下面的bar會(huì)覆蓋上面的bar方法
class Foo {
    bar() { return 1; }
    bar() { return 2; }
}

模塊

  • 使用模塊import/export輸入輸出代碼塊 [1]
  • 使用import時(shí),不要使用通配符,最好明確要導(dǎo)入的代碼塊 [0]
// 另外一個(gè)模塊文件,比如是index.js
export const firstName = 'rede';
export const lastName = 'li';
// 引用該模塊
// bad
// 按webpack 4+以上的版本會(huì)靜態(tài)分析index.js只導(dǎo)入需要的代碼塊,所以明確導(dǎo)入的代碼塊會(huì)更有利于減少打包體積
import * as index from './index';

console.log(index.firstName);
// best
// 原例子上還提到了一個(gè)good的寫法,不過(guò)看了下,感覺(jué)這種寫法最好,結(jié)合編譯器,還能減小文件代碼
import { firstName } from './index';

console.log(firstName);
  • 不要從import中直接導(dǎo)出(export) [0]
// bad
export { firstName as default } from './index';
// good
// 這樣更加明晰
import { firstName } from './index';

export default firstName;
  • 同一個(gè)路徑的導(dǎo)入只在一個(gè)地方導(dǎo)入,禁止重復(fù)導(dǎo)入 [1]
// bad
import { firstName } from './index';
import { lastName } from './index';

export default { firstName, lastName };
// good
import { firstName, lastName } from './index';

export default { firstName, lastName };
  • 不要export可變綁定 [1]
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
  • 如果一個(gè)文件只導(dǎo)出一個(gè)模塊,默認(rèn)export優(yōu)于命名export [1]
// bad
const foo = 3;
export { foo };
// good
const foo = 3;
export default { foo };
  • 將所有import導(dǎo)入放在非導(dǎo)入語(yǔ)句上面 [1]
// bad
import { firstName, lastName, backName } from './index';
backName();
import { name } from './test';

export default { firstName, lastName, name };
// good
import { firstName, lastName, backName } from './index';
import { name } from './test';

backName();

export default { firstName, lastName, name };
  • 多行導(dǎo)入應(yīng)該像多行數(shù)組那樣進(jìn)行縮進(jìn) [0]
// bad
import { firstName, lastName, year } from './main';

export { firstName, lastName, year };
// good
import {
    firstName,
    lastName,
    year,
} from './main';

export { firstName, lastName, year };
  • 禁止在模塊的import中使用Webpack加載語(yǔ)法 [1]
// bad
import fooSass from 'css!sass!foo.scss';
// good
import fooSass from 'foo.scss';

迭代器(Iterators)和生成器(Generators)

  • 不要使用iterators,請(qǐng)使用高階函數(shù),例如map、reduce而不是for-in、for-of這樣的循環(huán) [1]
// bad
// no-restricted-syntax這個(gè)檢查是禁用了特定語(yǔ)法
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
for (const num of numbers) {
    sum += num;
}

export default { sum };
// good
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
numbers.forEach((num) => {
    sum += num;
});

export default { sum };
// best
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((total, num) => total + num, 0);

export default { sum };
// bad
// 此時(shí)檢查并不會(huì)報(bào)錯(cuò),只是語(yǔ)法建議,不過(guò)和下面的good、best進(jìn)行比較,best寫法更加明晰
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = [];
for (let i = 0; i < numbers.length; i + 1) {
    increasedByOne.push(numbers[i] + 1);
}

export default { increasedByOne };
// good
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = [];
numbers.forEach((num) => {
    increasedByOne.push(num + 1);
});

export default { increasedByOne };
// best
const numbers = [1, 2, 3, 4, 5];
const increasedByOne = numbers.map(num => num + 1);

export default { increasedByOne };
  • 現(xiàn)在不要使用generators(生成器) [0]
// 下面的代碼來(lái)源阮一峰老師的《ECMAScript 6 入門》的Generator 函數(shù)的語(yǔ)法,在瀏覽器中運(yùn)行是可以正常運(yùn)行的,但按照建議規(guī)范的建議是因?yàn)楝F(xiàn)在還沒(méi)有好的方式轉(zhuǎn)為ES5,所以不建議使用
function* helloWorldGenerator() {
    yield 'hello';
    yield 'world';
    return 'ending';
}

const hw = helloWorldGenerator();
export default { hw };
  • 如果使用了generators,function*是定義generators的專用語(yǔ)法,不可以把function和*分開(kāi)寫 [1]

屬性

  • 使用點(diǎn)語(yǔ)法來(lái)訪問(wèn)對(duì)象屬性 [1]
// bad
const luke = {
    jedi: true,
    age: 28,
};
const isJedi = luke['jedi'];

export default { isJedi };
// good
const luke = {
    jedi: true,
    age: 28,
};
const isJedi = luke.jedi;

export default { isJedi };
  • 當(dāng)通過(guò)變量訪問(wèn)屬性時(shí)要使用中括號(hào) [0]
const luke = {
    jedi: true,
    age: 28,
};
 
function getProp(prop) {
    return luke[prop];
}
 
const isJedi = getProp('jedi');

export default { isJedi };
  • 求冪運(yùn)算使用求冪運(yùn)算符 [1]
// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;

變量

  • 總是使用const或let聲明變量,避免全局變量污染 [1]
  • 每個(gè)便利在聲明時(shí),都要使用const或let [1]
// bad
const items = 'rede',
    goSportsTeam = true,
    dragonball = 'z';

export { items, goSportsTeam, dragonball };
// good
const items = 'rede';
const goSportsTeam = true;
const dragonball = 'z';

export { items, goSportsTeam, dragonball };
  • 將所有的const和let的定義分組 [0]
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
  • 在需要的地方再對(duì)變量進(jìn)行分配 [0]
// bad
// 這里的checkName中name會(huì)調(diào)用getName函數(shù),但是name并不是總會(huì)返回,如果hasName為test時(shí),并不會(huì)返回,所以,應(yīng)該把對(duì)name的定義延后
const getName = function backName() {
    return 'rede';
};

function checkName(hasName) {
    const name = getName();

    if (hasName === 'test') {
        return false;
    }

    return name;
}
// good
const getName = function backName() {
    return 'rede';
};

function checkName(hasName) {
    if (hasName === 'test') {
        return false;
    }
    const name = getName();

    return name;
}
  • 禁止使用連續(xù)賦值 [1]
// bad
const a = b = c = 1;
// good
const a = 1;
const b = a;
const c = a;
  • 避免使用++或--運(yùn)算符 [1]
// bad
let num = 1;
num++;
--num;
// good
let num = 1;
num += 1;
num -= 1;
  • 在=號(hào)前后要避免進(jìn)行換行,如果變量名超過(guò)最長(zhǎng)限制,要統(tǒng)一換行方式 [0]
// bad
// 此時(shí)的換行是沒(méi)有必要的
const foo
  = 'superLongLongLongLongLongLongLongLongString';
// good
const foo = 'superLongLongLongLongLongLongLongLongString';  
// bad
const foo =
  superLongLongLongLongLongLongLongLongFunctionName();
// good
const foo = (
  superLongLongLongLongLongLongLongLongFunctionName()
);

變量提升

  • var聲明會(huì)被提升至他們作用域的頂部,但相應(yīng)的賦值不會(huì)提升,let和const的聲明并不會(huì)提升,因?yàn)槠湫纬闪艘粋€(gè)暫時(shí)性死區(qū)
(function example() {
  console.log(declaredButNotAssigned); // => undefined
  var declaredButNotAssigned = true;
})()  
...
function example() {
  console.log(declaredButNotAssigned); // => throws a ReferenceError
  console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
  const declaredButNotAssigned = true;
}
  • 匿名函數(shù)表達(dá)式的變量也會(huì)被提升,但函數(shù)體不會(huì)被提升
function example() {
    console.log(anonymous); // => undefined
 
    anonymous(); // => TypeError anonymous is not a function 輸入錯(cuò)誤,anonymous 不是一個(gè)函數(shù)
 
    var anonymous = function () {
    console.log('anonymous function expression');
    };
}
  • 命名的函數(shù)表達(dá)式變量也會(huì)被提升,但函數(shù)體不會(huì)被提升
function example() {
    console.log(named); // => undefined
 
    named(); // => TypeError named is not a function,輸入錯(cuò)誤,named 不是一個(gè)函數(shù)
 
    superPower(); // => ReferenceError superPower is not defined, ReferenceError(引用錯(cuò)誤)superPower 未定義
    
    var named = function superPower() {
        console.log('Flying');
    };
}
  • 函數(shù)聲明的名詞和函數(shù)體都會(huì)被提升
function example() {
    superPower(); // => Flying
 
    function superPower() {
    console.log('Flying');
    }
}

比較運(yùn)算符和等號(hào)

  • 使用===和!==,避免使用==和!= [1]

  • 諸如if語(yǔ)句之類的條件語(yǔ)句會(huì)把其中的值強(qiáng)制進(jìn)行布爾值轉(zhuǎn)換,遵循以下簡(jiǎn)單規(guī)則

    • Objects求值為true
    • Undefined和Null求值為false
    • Numbers如果是+0,-0或NaN求值為false,其他為true
    • Strings如果是''求值為false,其他為true
  • 對(duì)于布爾值使用簡(jiǎn)寫,但對(duì)于數(shù)字和字符串要進(jìn)行顯式比較 [0]

const isValid = true;
// bad
// isValid是布爾值
if (isValid === true) {
    // ...
}
// good
if (isValid) {
    // ...
}
...
const name = 'rede';
// bad
// 比如代碼塊中依賴對(duì)name是否為空進(jìn)行相關(guān)邏輯,這時(shí)省略對(duì)''進(jìn)行比較是不會(huì)影響功能的,但是對(duì)代碼的可讀性會(huì)產(chǎn)生影響
if (name) {
    // ...
}
// good
if (name !== '') {
    // ...
}
...
const collection = [];
// bad
if (collection.length) {
  // ...
}
// good
if (collection.length > 0) {
  // ...
}
  • 在case和default中,如果要?jiǎng)?chuàng)建包含詞法聲明的語(yǔ)句塊(let、const、function和class)要使用大括號(hào)進(jìn)行包裹 [1]
// bad
// 詞法聲明在整個(gè)switch語(yǔ)句都是可見(jiàn)的,在多個(gè)case子句試圖定義相同變量時(shí)會(huì)報(bào)no-redeclare(禁止重新聲明變量)
const num = 1;
switch (num) {
case 1:
    let name = 'rede';
    break;
case 2:
    let name = 'tom';
    break;
}
// good
const num = 1;
switch (num) {
case 1: {
    const name = 'rede';
    break;
}
case 2: {
    const name = 'tom';
    break;
}
}
  • 使用三元操作時(shí),避免進(jìn)行嵌套 [1]
// bad
// 會(huì)降低代碼的易讀性
const bar = 'bar';
const bing = 'bind';
const foo = bar === 'bars' ? (bing === 'bing' ? 'bars bing' : 'bars bind') : 'bar';

export default { foo };
// good
const bar = 'bar';
const bing = 'bind';
const barVal = bar === 'bars' ? 'bars' : 'bar';
const foo = bing === 'bing' ? `${barVal} bing` : `${barVal} bind`;

export default { foo };
  • 避免使用一些不必要的三元運(yùn)算 [1]
// bad
const answer = 1;
const isYes = answer === 1 ? true : false;

export default { isYes };
// good
const answer = 1;
const isYes = answer === 1;

export default { isYes };
  • 當(dāng)多個(gè)運(yùn)算符混合在一個(gè)語(yǔ)句中,要添加適當(dāng)?shù)睦ㄌ?hào),不要將**和%與+、-,*,/混在一起使用,能提高代碼的可讀性 [1]
// bad
const a = 1;
const b = 0;
const c = 4;
const d = 3;
const foo = a && b < 0 || c > 0 || d + 1 === 0;
const bar = a ** b - 5 % d;

export default { foo, bar };
// good
const a = 1;
const b = 0;
const c = 4;
const d = 3;
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
const bar = (a ** b) - (5 % d);

export default { foo, bar };

代碼塊

  • 使用大括號(hào)包裹多行代碼,單行代碼可以強(qiáng)制直接出現(xiàn)不需要換行 [1]
const test = true;
let boo = '';
// bad
if (test)
    boo = 'boo';
// good
if (test) boo = 'boo';    

export default { boo };
  • 如果通過(guò)if 和 else使用多行代碼塊,要把else放在if代碼塊閉合括號(hào)的同一行 [1]
// bad
const name = 'rede';
let foo = '';
if (name === 'rede') {
    foo = 'foo'
}
else {
    foo = 'bar';
}

export default { foo };
// good
const name = 'rede';
let foo = '';
if (name === 'rede') {
    foo = 'foo';
} else {
    foo = 'bar';
}

export default { foo };
  • 如果一個(gè)if代碼塊使用了return語(yǔ)句,后面的else可以省略,在else if塊中return可分成多個(gè)if來(lái)return [1]
// bad
function foo (val) {
    if (val) {
        return 'foo';
    } else {
        return 'bar';
    }
}

export default { foo };
// good
function foo(val) {
    if (val) {
        return 'foo';
    }
    return 'bar';
}

export default { foo };
// bad
function foo(val) {
    if (val === 'foo') {
        return 'foo';
    } else if (val === 'bar') {
        return 'bar';
    } else if (val === 'test') {
        return 'test';
    }
    return 'rede';
}

export default { foo };
// good
function foo(val) {
    if (val === 'foo') {
        return 'foo';
    }
    if (val === 'bar') {
        return 'bar';
    }
    if (val === 'test') {
        return 'test';
    }
    return 'rede';
}

export default { foo };

控制語(yǔ)句

  • 如果控制語(yǔ)句太長(zhǎng)或超過(guò)最大行長(zhǎng)度,那么每個(gè)分組條件可以放單獨(dú)一行,但要注意把運(yùn)算符放在每行的起始處 [0]
// not good
const nv1 = 2;
const nv2 = 3;
const nv3 = 4;
const nv4 = 5;
const nv5 = 6;
const nv6 = 7;
let foo = '';
// not good
if (nv1 === 1 && nv2 === 2 && nv3 === 3 && nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';
// not good
if (nv1 === 1 && nv2 === 2 && nv3 === 3 && 
    nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';
// good 
if (nv1 === 1 && nv2 === 2 && nv3 === 3 
    && nv4 === 4 && nv5 === 5 && nv6 === 6) foo = 'foo';

export default { foo };

注釋

  • 多行注釋使用/**...*/ [0]
// good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
  • 單行注釋使用 // ,將單行注釋放在續(xù)注釋的語(yǔ)句上方。在注釋之前放置一個(gè)空行,除非它位于代碼塊的第一行。 [0]
// bad
const active = true;  // is current tab
 
// good
// is current tab
const active = true;
// bad
function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    const type = this.type || 'no type';
 
    return type;
}
 
// good
function getType() {
    console.log('fetching type...');
 
    // set the default type to 'no type'
    const type = this.type || 'no type';
 
    return type;
}
 
// also good
function getType() {
    // set the default type to 'no type'
    const type = this.type || 'no type';
 
    return type;
}
  • 所有注釋符和注釋內(nèi)容用一個(gè)空格隔開(kāi),讓它更容易閱讀 [0]
// bad
//is current tab
// good
// is current tab
// bad
/**
 *make() returns a new element
 *based on the passed-in tag name
 */
 // good
/**
 * make() returns a new element
 * based on the passed-in tag name
 */
  • 給注釋增加FIXME或TODO的前綴,可以幫助其他開(kāi)發(fā)者快速了解這個(gè)是否是一個(gè)需要重新復(fù)查的問(wèn)題,或是你正在為需要解決問(wèn)題提出的解決方案,有別于常規(guī)注釋 [0]

  • 使用 // FIXME來(lái)標(biāo)識(shí)需要修正的問(wèn)題

  • 使用 // TODO來(lái)標(biāo)識(shí)需要實(shí)現(xiàn)的問(wèn)題

還可以使用 // XXX說(shuō)明注釋處代碼雖然實(shí)現(xiàn)了功能,但是實(shí)現(xiàn)的方法有待商榷,希望將來(lái)能改進(jìn),要改進(jìn)的地方會(huì)在說(shuō)明中簡(jiǎn)略說(shuō)明

空白

  • 使用一致的縮進(jìn) [1]

這里默認(rèn)是使用4個(gè)空格

  • 在大括號(hào)前放置一個(gè)空格 [1]
// bad
function test(){
    console.log('test');
}
// good
function test() {
    console.log('test');
}
// bad
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
 
// good
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
  • 在控制語(yǔ)句的小括號(hào)前放一個(gè)空格,在函數(shù)調(diào)用及聲明中,不在函數(shù)的參數(shù)列表前加空格 [1]
const isJedi = true;
// bad
if(isJedi) {
    console.log('dd');
}
// good
if (isJedi) {
    console.log('dd');
}
// bad
function fight () {
    console.log ('Swooosh!');
}
// good
function fight() {
    console.log('Swooosh!');
}
  • 使用空格把運(yùn)算符隔開(kāi) [1]
// bad
const x=y+5;
// good
const x = y + 5;
  • 在文件末尾插入一個(gè)空行 [1]
  • 長(zhǎng)方法鏈?zhǔn)秸{(diào)用時(shí)使用縮進(jìn),使用一個(gè)點(diǎn)開(kāi)頭,強(qiáng)調(diào)該行是一個(gè)方法調(diào)用,不是一個(gè)新的聲明 [1]

估計(jì)是怕鏈?zhǔn)秸{(diào)用太長(zhǎng)不方便看

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
$('#items').find('.selected').highlight().end()
    .find('.open')
    .updateCount();
  • 在語(yǔ)句塊后和下條語(yǔ)句前留一個(gè)空行 [0]
const foo = true;
let bar = '';
// bad
if (foo) {
    bar = 'bar';
}
export default { bar };
// good
if (foo) {
    bar = 'bar';
}

export default { bar };
  • 塊級(jí)代碼中禁用多余的空行 [1]
// bad
function bar() {
 
    console.log(foo);
 
}
// good
function bar() {
    console.log(foo);
}
  • 不要在圓括號(hào)前后加空格 [1]
// bad
if ( foo ) {
    console.log(foo);
}
// good
if (foo) {
    console.log(foo);
}
  • 不要在中括號(hào)前后添加空格 [1]
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
  • 在大括號(hào)前后添加空格 [1]
// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };
  • 避免有超過(guò)100個(gè)字符(包括空格)的代碼行,如果有超過(guò),要適當(dāng)考慮把代碼進(jìn)行換行 [1]

保證代碼的可讀性和可維護(hù)性

// bad
const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
// good
const foo = jsonData
    && jsonData.foo
    && jsonData.foo.bar
    && jsonData.foo.bar.baz
    && jsonData.foo.bar.baz.quux
    && jsonData.foo.bar.baz.quux.xyzzy;
// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
// good
$.ajax({
  method: 'POST',
  url: 'https://airbnb.com/',
  data: { name: 'John' },
})
  .done(() => console.log('Congratulations!'))
  .fail(() => console.log('You have failed this city.'));    

逗號(hào)

  • 在行開(kāi)頭處不要使用逗號(hào) [1]
// bad
const hero = {
    firstName: 'Ada'
    , lastName: 'Lovelace'
    , birthYear: 1815
    , superPower: 'computers'
};
// good
const hero = {
    firstName: 'Ada',
    lastName: 'Lovelace',
    birthYear: 1815,
    superPower: 'computers',
};
  • 在結(jié)尾添加逗號(hào) [1]
// bad
const hero = {
    firstName: 'Ada',
    lastName: 'Lovelace',
    birthYear: 1815,
    superPower: 'computers'
};
// good
const hero = {
    firstName: 'Ada',
    lastName: 'Lovelace',
    birthYear: 1815,
    superPower: 'computers',
};

因?yàn)橥ㄟ^(guò)git diff時(shí),能體現(xiàn)出這些差異

// bad - 沒(méi)有結(jié)尾逗號(hào)的 git diff 差異比較
const hero = {
     firstName: 'Florence',
-    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing']
};

// good - 有結(jié)尾逗號(hào)的 git diff 差異比較
const hero = {
     firstName: 'Florence',
     lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};

分號(hào)

  • 禁止使用自動(dòng)分號(hào)插入,都要加入分號(hào) [1]

類型轉(zhuǎn)換

  • 在聲明語(yǔ)句的開(kāi)始處就執(zhí)行強(qiáng)制類型轉(zhuǎn)換
  • 轉(zhuǎn)為字符串時(shí),禁止原始包裝實(shí)例
const reviewScore = 9;
// bad
// 此時(shí)typeof totalScore為Object并不是string
const totalScore = new String(reviewScore); 
// bad
// 調(diào)用的事 reviewScore.valueOf()
const totalScore = reviewScore + ''; 
// bad
// 不能保證返回一個(gè)字符串
const totalScore = reviewScore.toString(); 
// good
const totalScore = String(reviewScore);
  • 轉(zhuǎn)為數(shù)字類型時(shí),禁止使用原始包裝實(shí)例,如果使用parseInt要必須有基數(shù) [1]
const inputValue = '4';
// bad
// 不要使用原始包裝實(shí)例
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// good
const val = Number(inputValue);
// bad
// 沒(méi)基數(shù)
const num = parseInt('071');
// good
const num = parseInt('071', 10);
  • 避免使用按位運(yùn)算符 [1]
// bad
const val = inputValue >> 0;
  • 進(jìn)行布爾值轉(zhuǎn)換時(shí),避免使用原始包裝實(shí)例 [1]
const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;

命名規(guī)則

  • 避免使用單字母名詞 [0]
  • 當(dāng)命名對(duì)象,函數(shù)和實(shí)例時(shí)使用駝峰式命名 [1]
// bad
// 一般命名分為駝峰和下劃線拼寫寫法,這里建議使用駝峰式寫法
const this_is_my_object = {};
// good
const thisIsMyObject = {};
  • 當(dāng)命名構(gòu)造函數(shù)或類時(shí)使用PascalCase式命名(帕斯卡拼寫法,即首字母大寫) [0]
// bad
function user(options) {
    this.name = options.name;
}
 
const bad = new user({
    name: 'nope',
});
// good
class User {
    constructor(options) {
    this.name = options.name;
    }
}
 
const good = new User({
    name: 'yup',
});
  • 變量禁止使用下劃線開(kāi)頭或結(jié)尾 [1]
// bad
const __firstName__ = 'Panda';
// good
const firstName = 'Panda';
  • 不要存儲(chǔ)this引用 [0]
// bad
function foo() {
    const self = this;
    return function () {
        console.log(self);
    };
}
// good
function foo() {
    return () => {
        console.log(this);
    };
}
  • 文件名應(yīng)與其默認(rèn)導(dǎo)出的名詞完全匹配 [0]
// 文件1
class CheckBox {
    // ...
}
export default CheckBox;
 
// 文件2
export default function fortyTwo() { return 42; }
 
// 文件3
export default function insideDirectory() {}

// good
import CheckBox from './CheckBox'; // export/import/filename 單詞首字母大寫命名
import fortyTwo from './fortyTwo'; // export/import/filename 駝峰式命名
import insideDirectory from './insideDirectory'; 
  • 當(dāng)導(dǎo)出一個(gè)默認(rèn)函數(shù)時(shí)使用駝峰式命名,文件名應(yīng)該和你的函數(shù)名字一致 [0]
function makeStyleGuide() {
    // ...
}
 
export default makeStyleGuide;
...
文件名為makeStyleGuide
  • 當(dāng)導(dǎo)出一個(gè)構(gòu)造函數(shù)/類/單例/函數(shù)庫(kù)/純對(duì)象時(shí)使用PascalCase式命名 [0]
  • 首字母縮寫的詞,應(yīng)該總是全部大寫,或全部小寫 [0]
// bad
import SmsContainer from './containers/SmsContainer';
// bad
const HttpRequests = [
    // ...
];
// good
import SMSContainer from './containers/SMSContainer';
 
// good
const HTTPRequests = [
    // ...
];
// also good
const httpRequests = [
    // ...
];
// best
import TextMessageContainer from './containers/TextMessageContainer';
  • 如果變量是被導(dǎo)出的,或者只是常量,或者可以確信變量不會(huì)改變,可以使用大寫,使用大寫的變量可以幫助使用者確定使用的變量不會(huì)改變 [0]

存取器

  • 屬性的存取器不是必須
  • 避免使用js的getters/setters,因?yàn)闀?huì)導(dǎo)致意想不到的副作用,而且會(huì)很難測(cè)試維護(hù),可以使用存取器函數(shù),使用getVal及setVal [0]
// bad
class Dragon {
    constructor(val) {
        this.name = val;
        this.year = 0;
    }
    get age() {
        // ...
        return this.year;
    }

    set age(value) {
        // ...
        this.year = value;
    }
}
// good
class Dragon {
    constructor(val) {
        this.name = val;
        this.year = 0;
    }
    getAge() {
        // ...
        return this.year;
    }

    setAge(value) {
        // ...
        this.year = value;
    }
}
  • 也可以創(chuàng)建 get() 和 set() 函數(shù), 但要保持一致 [0]

標(biāo)準(zhǔn)庫(kù)

  • 使用Number.isNaN代替全局isNaN [1]
// bad
// no-restricted-globals禁用特定的全局變量
isNaN('1.2');
// good
Number.isNaN('1.2.3');
  • 使用Number.isFinite代替isFinite [1]
// bad
isFinite('2e3');
// good
Number.isFinite('2e3');

airbnb規(guī)范標(biāo)準(zhǔn)-css部分

這部分內(nèi)容是建議

  • 格式

    • 使用 2 個(gè)空格作為縮進(jìn)。
    • 類名建議使用破折號(hào)代替駝峰法。如果你使用 BEM,也可以使用下劃線(參見(jiàn)下面的 OOCSS 和 BEM)。
    • 不要使用 ID 選擇器。
    • 在一個(gè)規(guī)則聲明中應(yīng)用了多個(gè)選擇器時(shí),每個(gè)選擇器獨(dú)占一行。
    • 在規(guī)則聲明的左大括號(hào) { 前加上一個(gè)空格。
    • 在屬性的冒號(hào) : 后面加上一個(gè)空格,前面不加空格。
    • 規(guī)則聲明的右大括號(hào) } 獨(dú)占一行。
    • 規(guī)則聲明之間用空行分隔開(kāi)。
  • 注釋

    • 建議使用行注釋 (在 Sass 中是 //) 代替塊注釋。
    • 建議注釋獨(dú)占一行。避免行末注釋。
    • 給沒(méi)有自注釋的代碼寫上詳細(xì)說(shuō)明,比如:
      • 為什么用到了 z-index
      • 兼容性處理或者針對(duì)特定瀏覽器的 hack
  • 不建議使用ID選擇器

ID選擇器會(huì)帶來(lái)不必要的優(yōu)先級(jí),而且ID選擇器是不可復(fù)用的

  • JavaScript鉤子

避免在Css和JavaScript中綁定相同的類,不利于后續(xù)維護(hù),因?yàn)閷懚ê缶拖喈?dāng)于樣式和js文件綁定了,改樣式名詞會(huì)造成頁(yè)面邏輯出錯(cuò)。如果要涉及到與樣式有關(guān)的操作,添加.js-前綴

  • 定義邊框無(wú)樣式時(shí),使用0代替none

  • 不要讓嵌套選擇器深度超過(guò)3層

Google HTML/CSS代碼風(fēng)格

并非按條條寫,只選取了一部分

HTML規(guī)則

  • 對(duì)于圖片和其他媒體文件,樣式表和腳本,請(qǐng)盡量使用https協(xié)議,除非相關(guān)文件不支持
  • 標(biāo)簽,屬性,屬性值,css選擇器,屬性,和屬性值只使用小寫
  • 刪除行尾不必要的空格
  • 指定頁(yè)面編碼為utf-8

<meta charset="utf-8">

  • 添加合理的注釋,說(shuō)明相關(guān)代碼是做什么的,只在關(guān)鍵代碼處添加相關(guān)注釋,避免過(guò)多添加增加HTML和CSS的代碼量
    • 使用TODO注釋,說(shuō)明代碼功能
  • 文檔類型首選HTML5標(biāo)準(zhǔn)

<!DOCTYPE html>

  • 保證HTML代碼的有效性,可以借助W3C HTML validator分析頁(yè)面,修正一些明顯的錯(cuò)誤

這個(gè)工具會(huì)分析出靜態(tài)文件不符合語(yǔ)意化的地方,不過(guò)這個(gè)檢查標(biāo)準(zhǔn)比較嚴(yán)格,僅供參考

  • 根據(jù)HTML元素的語(yǔ)義使用相關(guān)元素
  • 為有意義的多媒體元素提供備選文案

比如一個(gè)詳情頁(yè)的產(chǎn)品圖,就是頁(yè)面中有意義的圖片,要保證這類多媒體元素要有備選文案,一方面是保證元素在加載不出時(shí),頁(yè)面不至于報(bào)錯(cuò),另一方面也是給盲人提供提示文字,但如果是一些類似背景圖片的元素就沒(méi)必要添加備選文案

  • 將行為和表現(xiàn)分開(kāi)

嚴(yán)格遵循結(jié)構(gòu),表現(xiàn)和行為的分離,盡量保證三者交互保持最低,確保所有表現(xiàn)都放到樣式表中,所有行為都放到腳本中,要盡量減少外鏈

// bad
<!DOCTYPE html>
<title>HTML sucks</title>
<link rel="stylesheet" href="base.css" media="screen">
<link rel="stylesheet" href="grid.css" media="screen">
<link rel="stylesheet" href="print.css" media="print">
<h1 style="font-size: 1em;">HTML sucks</h1>
<p>I’ve read about this on a few sites but now I’m sure:
  <u>HTML is stupid!!1</u>
<center>I can’t believe there’s no way to control the styling of
  my website without doing everything all over again!</center>
...
// good
<!DOCTYPE html>
<title>My first CSS-only redesign</title>
<link rel="stylesheet" href="default.css">
<h1>My first CSS-only redesign</h1>
<p>I’ve read about this on a few sites but today I’m actually
  doing it: separating concerns and avoiding anything in the HTML of
  my website that is presentational.
<p>It’s awesome!
  • 省略樣式表和腳本類型的type屬性
  • 引用屬性值時(shí),使用雙引號(hào)
  • 對(duì)class命名時(shí)要能保證光從名字就能看出是干什么用的,命名也要簡(jiǎn)短易懂

CSS規(guī)則

  • 避免使用CSS類型選擇器
// bad
div.error {}
// good
.error {}
  • 寫屬性值時(shí)盡量使用縮寫
// bad
padding-bootom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;
...
// good
padding: 0 1em 2em;
  • 除非必要,否則省略0后面的單位
flex: 0px; 
flex: 1 1 0px; /* IE11下會(huì)要求添加單位 */
  • 省略0開(kāi)頭小數(shù)前面的0
// bad
font-size: 0.8em;    
// good
font-size: .8em;
  • 十六進(jìn)制盡可能使用3個(gè)字符
// bad
color: #eebbcc;
// good
color: #ebc;
  • 用-連接命名,增進(jìn)對(duì)名字的理解和查找
// bad
.demoimage {}
.error_status {}
// good
.video-id {}
  • 避免使用css hacks
  • 選擇器在大括號(hào)前要添加一個(gè)空格
// bad
.test{
    ...
}
// good
.test {
    ...
}
  • 在每個(gè)聲明尾部都加上分號(hào)
// bad
.test {
    display: block;
    height: 100px
}
// good
.test {
    display: block;
    height: 100px;
}
  • 在屬性名冒號(hào)結(jié)束后添加一個(gè)空格
// bad
h3 {
    font-size:16px;
}
// good
h3 {
    font-size: 16px;
}
  • 當(dāng)有多個(gè)選擇器時(shí),每個(gè)選擇器都要獨(dú)立新行
// bad
h1, h2, h3 {

}
// good
h1,
h2,
h3 {

}
  • 多個(gè)規(guī)則之間要隔行
// bad
html {
    ...
}
body {
    ...
}
// good
html {
    ...
}

body {
    ...
}

這些規(guī)則表明看起來(lái)會(huì)增加css代碼量,但實(shí)際上我們現(xiàn)在結(jié)合webpack等工具,會(huì)對(duì)css進(jìn)行壓縮,多余的空格會(huì)被刪除,這些規(guī)則方便后續(xù)代碼的維護(hù)

  • 按模塊功能寫注釋
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容