ES6 實踐規範

美團點評點餐發表於2017-06-05

前言:團隊基於ES6和Eslint規則規定程式碼規範,本文的目的是梳理和總結團隊現在實行的規範。

作者:鄭靈華,點餐秒付終端團隊成員

目錄

一、Eslint檢測ES6規範配置

  1. 編碼格式規範
  2. 宣告唯一性
  3. 初始化定義規範
  4. 程式碼編寫注意事項
  5. 派生類相關

二、Airbnb規範節選

  1. 箭頭函式
  2. 構造器
  3. 迭代遍歷
  4. 屬性定義
  5. 解構
  6. 函式

三、參考資料


一、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)

來源:https://juejin.im/post/5934ff6d2f301e005861422f

相關文章