前言:團隊基於ES6和Eslint規則規定程式碼規範,本文的目的是梳理和總結團隊現在實行的規範。
作者:鄭靈華,點餐秒付終端團隊成員
目錄
一、Eslint檢測ES6規範配置
- 編碼格式規範
- 宣告唯一性
- 初始化定義規範
- 程式碼編寫注意事項
- 派生類相關
二、Airbnb規範節選
- 箭頭函式
- 構造器
- 迭代遍歷
- 屬性定義
- 解構
- 函式
三、參考資料
一、Eslint檢測ES6規範配置
1. 編碼格式規範
a.規定箭頭函式強制大括號
b.規定箭頭函式引數是否要使用括號
c.規定箭頭函式的箭頭前後的空格規範
d.generator 函式中 號周圍的空格
e.規定rest引數和擴充套件運算子與他們的引數之間的空格
f.禁止模板字面量中的花括號出現括號
g.強制在 yield 表示式中 * 後面使用空格
a.規定箭頭函式強制大括號
//Eslint檔案配置項'arrow-body-style': ['error', 'as-needed', {
requireReturnForObjectLiteral: false,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
as-needed | 當大括號是可省略的,強制不使用 | |
requireReturnForObjectLiteral | 不需要顯式返回物件字面量 | 必須與as-needed 搭配使用 |
//專案中正確使用事例//可以省略大括號let foo = () =>
0;
//不用顯式返回物件字面量let foo = () =>
({
bar: 0
});
//錯誤對比:let foo = () =>
{
return ({bar: 0
});
};
複製程式碼
b.規定箭頭函式引數是否要使用括號
//Eslint檔案配置項'arrow-parens': ['error', 'as-needed', {
requireForBlockBody: true,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
as-needed | 當只有一個引數時允許省略圓括號 | |
requireForBlockBody | 當函式體在一個指令塊中,引數必須用圓括號包含 | 作為as-needed補充。以函式體是否被 {
} 包括快速判斷 |
//專案實踐正確例子// 只有一個引數允許省略a.map(x =>
{
return x * x;
});
// requireForBlockBody 引數作為補充,上述程式碼修改成a.map((x) =>
{
return x * x;
});
複製程式碼
c.箭頭函式的箭頭前後的空格規範
'arrow-spacing': ['error', {
before: true, after: true
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
before | 箭頭前面有空格 | |
after | 箭頭後面有空格 |
//專案應用a =>
a;
複製程式碼
d.generator 函式中 * 號周圍的空格
'generator-star-spacing': ['error', {
before: false, after: true
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
before | *前面沒有空格 | |
after | *後面有空格 |
//專案正確使用示例function* generator() {
}複製程式碼
e.不允許rest引數和擴充套件運算子與他們的引數之間有空格
'rest-spread-spacing': ['error', 'never']複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
never | 符號和引數之間不能有空格 |
//rest引數let [a, b, ...arr] = [1, 2, 3, 4, 5]//擴充套件運算子function fn(){
}fn(...args)複製程式碼
f.禁止模板字面量中的花括號出現括號
'template-curly-spacing': 'error'複製程式碼
//花括號裡面沒有括號`hello, ${people.name
}!`複製程式碼
g.強制在 yield 表示式中 後面使用空格
'yield-star-spacing': ['error', 'after']複製程式碼
function* generator() {
yield* other();
}複製程式碼
2. 宣告唯一性
a.不能修改類宣告的變數
b.禁止修改const宣告的變數
c.不允許類成員裡有重複的名稱
d.不要重複引入一個模組
e.禁止在import,export,解構賦值中重新命名和原有名字相同
a.不能修改類宣告的變數
'no-class-assign': 'error'複製程式碼
// 簡而言之,如果以class Name{
}形object-shorthand式出現,那麼Name不能做任何更改和賦值// 下面例子是正確的。因為A至始至終只是變數let A = class {
b() {
A = 0;
console.log(A);
}
}console.log(A);
//classlet Foo = new A();
Foo.b();
//0console.log(A);
//0複製程式碼
b.禁止修改const宣告的變數
'no-const-assign': 'error'複製程式碼
c.不允許類成員裡有重複的名稱
'no-dupe-class-members': 'error'複製程式碼
d.不要重複引入一個模組
'no-duplicate-imports': 'off'複製程式碼
//同一個模組引入兩個變數應該寫在一個大括號裡面import {
merge, find
} from 'module';
複製程式碼
e.禁止在import,export,解構賦值中重新命名和原有名字相同
'no-useless-rename': ['error', {
ignoreDestructuring: false, ignoreImport: false, ignoreExport: false,
}]複製程式碼
//形如{
foo as foo
}和{
bar: bar
}並沒有起到重新命名的作用,所以應該禁止這種冗餘書寫import {
foo as bar
} from "baz";
export {
foo as bar
} from "foo";
let {
[foo]: foo
} = bar;
複製程式碼
3. 初始化定義規範
a.Symbol型別不能用new關鍵字
b.Symbol定義的時候增加描述語言,便於debug
c.generator函式裡面一定要有yield
d.使用 let 或 const 而不是 var
e.禁止在字面量宣告無用的計算屬性
f.若變數不會再次賦值,使用const宣告
a.Symbol型別不能用new關鍵字
'no-new-symbol': 'error'複製程式碼
//symbol應該以函式形式呼叫var foo = Symbol('foo');
複製程式碼
b.Symbol定義的時候增加描述語言,便於debug
'symbol-description': 'error'複製程式碼
let foo = Symbol("some description")複製程式碼
c.generator函式裡面一定要有yield
'require-yield': 'error'複製程式碼
d.使用 let 或 const 而不是 var
'no-var': 'error'複製程式碼
e.禁止在字面量宣告無用的計算屬性
'no-useless-computed-key': 'error'複製程式碼
//無用的["a"]計算屬性var foo = {['0+1,234']: "b"
};
//改寫成var foo = {
'0+1,234': 0
};
複製程式碼
f.若變數不會再次賦值,使用const宣告
'prefer-const': ['error', {
destructuring: 'any', ignoreReadBeforeAssign: true,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
destructuring | 解構賦值時,所有變數的型別都應該保持一致 | |
ignoreReadBeforeAssign | 忽略宣告和第一次賦值之間的變數 | 也就是不能先定義後賦值 |
//解構賦值時,值要麼都是const要麼都是let// a0是確定的,b沒有被賦值const {a: a0, b
} = obj;
const a = a0 + 1;
// a,b都是變數,所以解構賦值定義用letlet {a, b
} = obj;
a = a + 1;
b = b + 1;
//錯誤例子,在ignoreReadBeforeAssign=true時,timer的宣告和賦值之間的initialize()函式宣告會被省略。//從而會在setInterval處報錯函式undefinedlet timer;
function initialize() {
if (foo()) {
clearInterval(timer);
}
}timer = setInterval(initialize, 100);
//正確例子,只要宣告就要賦值!const timer = setInterval(initialize, 100);
function initialize() {
if (foo()) {
clearInterval(timer);
}
}複製程式碼
4.程式碼編寫注意事項
a. 避免箭頭函式和比較式混淆
b. 使用模板字面量而不是字串拼接
c. 使用擴充套件運算子(…)而非.apply()呼叫可變引數
d. 用rest引數(…變數名)替換arguments
e. 不允許使用parseInt()轉化2,8,16進位制
f. 要求使用箭頭函式進行回撥
g. 物件字面量語法簡寫
a.避免箭頭函式和比較式混淆
'no-confusing-arrow': ['error', {
allowParens: true,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
allowParens | 放寬標準,允許箭頭函式使用括號 | 不強制必須用return返回 |
//如果是嚴格模式,會發現即使用圓括號包含,也要用return區分箭頭函式和三元運算比較var x = a =>
{
return 1 ? 2 : 3;
};
var x = (a) =>
{
return 1 ? 2 : 3;
};
//如果allowParens=true,則放寬標準var x = a =>
(1 ? 2 : 3);
var x = (a) =>
(1 ? 2 : 3);
複製程式碼
b.使用模板字面量而不是字串拼接
'prefer-template': 'error'複製程式碼
let str = `Hello, ${name
}!`複製程式碼
c.使用擴充套件運算子(…)而非.apply()呼叫可變引數
'prefer-spread': 'error'複製程式碼
//求出一個陣列最大元素Math.max.apply(null, [14, 3, 77])//等效於Math.max(...[14, 3, 77])//等同Math.max(14, 3, 77)複製程式碼
d.用rest引數(…變數名)替換arguments
'prefer-rest-params': 'error'複製程式碼
//rest運算子可以提供一個真正的陣列,能顯式表示引數//而arguments是一個物件,操作中要通過call等手段呼叫陣列方法function foo(...args) {
console.log(args);
}複製程式碼
e. 不允許使用parseInt()轉化2,8,16進位制
'prefer-numeric-literals': 'error'複製程式碼
//只針對2,8,16進位制使用//數字轉化成其他進位制或者是變數轉化還是用parseInt0b111110111 === 503;
0o767 === 503;
0x1F7 === 503;
parseInt(1, 3);
parseInt(foo, 2);
複製程式碼
f. 要求使用箭頭函式進行回撥
'prefer-arrow-callback': ['error', {
allowNamedFunctions: false, allowUnboundThis: true,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
allowNamedFunctions | 如果回撥函式裡面是命名函式則報錯 | |
allowUnboundThis | 不使用bind()指定this,規則會動態標記this的使用 |
//直接使用this,而不是bind(this)//回撥函式裡是匿名函式foo(function() {
return this.a;
});
複製程式碼
g.物件字面量語法簡寫
'object-shorthand': ['error', 'always', {
ignoreConstructors: false, avoidQuotes: true,
}]複製程式碼
引數 | 引數說明 | 備註 |
---|---|---|
always | 能簡寫就簡寫 | |
ignoreConstructors | 建構函式不能省略 | 必須指定第一個引數 |
avoidQuotes | 物件鍵是字串時,用長格式 | 必須指定第一個引數 |
// 因為foo物件兩個鍵都是string,所以後面不能省略var foo = {
"bar-baz": function() {
}, "qux": qux
}複製程式碼
5.派生類相關
a. 建構函式必須呼叫 super
b. 禁止不必要的建構函式
c. 派生類函式構造器禁止在super()之前使用this
a. 建構函式必須呼叫 super
'constructor-super': 'error'複製程式碼
//派生類中建構函式必須呼叫,非派生類的建構函式不能呼叫super()class A {
constructor() {
}
}class A extends B {
constructor() {
super();
}
}複製程式碼
b. 禁止不必要的建構函式
'no-useless-constructor': 'error'複製程式碼
//簡單講便是構造器裡面一定要有執行的邏輯程式碼class A {
constructor () {
doSomething();
}
}//如果沒有特別的邏輯,則類返回空即可class A {
}複製程式碼
c. 派生類函式構造器禁止在super()之前使用this
'no-this-before-super': 'error複製程式碼
//否則會返回引用錯誤(reference error)class A extends B {
constructor() {
super();
this.a = 0;
// OK, this is after `super()`.
}
}複製程式碼
二、Airbnb規範節選
1. 箭頭函式
- 當函式特別簡單,並且只有一個引數,可省略花括號、圓括號、return
- 提高鏈式呼叫可讀性,不斷傳遞this
- 但是結果需要回傳一個物件時,不能省略return
2. 構造器
- 用class替代prototype
//用class更為簡潔class Foo {
constructor(){
} getVal(){
}
}//等價於Foo.prototype = {
getVal(){
}
}複製程式碼
- 用extends實現繼承,不直接操作prototype
- class能實現模組開發,並且能通過返回this實現鏈式呼叫
- 類的資料型別就是函式,類本身就指向建構函式
class Foo {
}typeof Foo;
// FunctionFoo === Foo.prototype.constructor;
//true複製程式碼
3. 迭代遍歷
- ES6提供的遍歷器Iterator,適合於部署了Symbol.iterator屬性的資料結構,可以用for…of遍歷成員
- for…in遍歷object所有可列舉物件,包含當前物件和原型上
- for…of遍歷collection物件,並不適用所有object,視乎其是否有[Symbol.iterator]屬性。且只遍歷當前物件
- Airbnb推薦使用高階函式例如 map() 和 reduce() 替代 for-of,便於處理函式回撥
4. 屬性定義
- 如果物件具有動態屬性名,在一開始使用可計算的屬性名稱,保證所有屬性定義在一起
const obj = {
id: 5, name: 'San Francisco', [getKey('enabled')]: true,
}複製程式碼
5. 解構
- 推薦使用解構賦值,減少臨時變數的引入
- 需要回傳多個值時,使用物件解構,而不是陣列解構,方便呼叫時可以忽略順序
// 陣列解構=>
呼叫時回撥資料的順序是一一對應的function processInput(input) {
// then a miracle occurs return [left, right, top, bottom];
}const [left, __, top] = processInput(input);
// 物件解構=>
不考慮順序,只要保證變數必須與屬性同名function processInput(input) {
// then a miracle occurs return {
left, right, top, bottom
};
}const {
left, right
} = processInput(input);
複製程式碼
- 物件的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變數。真正被賦值的是後者,而不是前者。
let {
foo: baz
} = {
foo: "aaa", bar: "bbb"
};
baz;
// "aaa"foo;
// error: foo is not defined複製程式碼
6. 函式
- 使用函式宣告代替函式表示式,便於在呼叫棧識別,避免整個函式被提升。同時,箭頭函式可以取代函式表示式
- 不要把引數命名為 arguments。這將取代原來函式作用域內的 arguments 物件
- 不要使用 arguments。用 rest 語法 … 替代
- 推薦: 函式引數應該指定預設值
//bad code,如果是Boolean,傳進去甚至會改變opts的型別function handleThings(opts) {
opts = opts || {
};
}//Airbnb 推薦寫法,初始化引數function handleThings(opts = {
}) {
}複製程式碼
三、參考資料
Airbnb JavaScript規範
阮一峰-ES6入門教程
小問ES6理解-進階版
ES6 In Depth (深入淺出ES6)