點燈坊

失くすものさえない今が強くなるチャンスよ

ECMAScript 之 Coding Style

Sam Xiao's Avatar 2020-02-24

ECMAScript 是 Mutl-paradigm 語言,因此存在多種不同風格寫法,統一程式碼風格有助於團隊合作。

Tab

Space 2

ASI

// bad
import Vue from 'vue';
let x = x + 1;

// good
import Vue from 'vue'
let x = x + 1

ECMAScript 支援 ASI (Automatic Semicolon Insertion),會在尾部自動新增 ;,因此可省略。

Naming Convetion

Variable

// bad
let total_count = 10
let first-name = 'John'
let IsDone = true

// good
let totalCount = 10
let firstName = 'John'
let isDone = true

Primitive variable 使用 noun + camelCase 命名。

// bad
const totalCount = 10
const firstName = 'John'
const isDone = true

// good
const TOTAL_COUNT = 10
const FIRST_NAME = 'John'
const IS_DONE = true

Constant 使用 noun + all uppercase 命名,複合字以 _ 隔開。

const 只對 primitive 有效,對 object 與 array 無效

Object

// bad
let printedBook = {
  book_title: 'FP in JavaScript',
  BookPrice: 100
}

// good
let printedBook = {
  bookTitle: 'FP in JavaScript',
  bookPrice: 100
}

Object 與 property 使用 noun + camelCamel 命名。

Array

// bad
let printedBook = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

// good
let printedBooks = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

Array 使用 複數 noun + camelCase 命名。

Function

// bad
let fetch_books = () => {}
let fetch-books = () => {}
let FetchBooks = () => {}

// good
let fetchBooks = () => {}

Function name 使用 camelCase 命名。

// bad
let bookFetched = () => {}

// good
let multiply = (x, y) => x * y
let fetchBooks = () => {}

Function 使用 verb 或 verb + noun 命名方式:

  • 若回傳為單數,則 noun 為單數
  • 若回傳為 array,則 noun 為複數

單 verb 適合 helper function
verb + noun 適合一般 function

// bad
let fetchBooks = (page-num, PageSize) => {}

// good
let fetchBooks = (pageNum, pageSize) => {}

Parameter 使用 noun + camelCase 命名。

// bad
let insertLabel = () => {}

// good
let addLabel = () => {}

新增資料時使用 add 為 prefix 較簡短。

// bad
let getHistoryLabel = () => {}

// good
let fetchHistoryLabel = () => {}

get 適合 getter,fetch 適合 Knex 或 Axios 抓資料。

// bad
let updateLabel = () => {}

// good
let editLabel = () => {}

修改資料時使用 edit 為 prefix 較簡短。

// bad
let deleteLabel = () => {}

// good
let delLabel = () => {}

刪除資料時使用 del 為 prefix 較簡短。

// bad
let whereNo = no => () => {}
let makeWhereNo = no => () => {}

// good
let genWhereNo = no => () => {}

gen 適合 higher order function,專門 return function。

Class

// bad
let myBook = function(title) {
  this.title = title
}

class myBook {
  constructor(title, price) {
    this.title
  }
}

// good
let MyBook = function(title) {
  this.title = title
}

class MyBook {
  constructor(title, price) {
    this.title
  }
}

Constructor function 或 class 的名稱使用 noun + CamelCase。

ECMAScript 只有在 constructor function 或 class 使用 CamelCase,其他都用 camelCase

Variable

// bad
var x = 1

// good
let x = 1

var 會有 hoisting 與 function scope 問題,一律使用 let 無 hoisting 且為 {} block scope。

String

Single Quote

// bad
let str = "123"
let str = `123`

// good
let str = '123'

String 使用 single quote。

String Concatenation

// bad
let key = 'pkg/status/' + pId + '/' + dId

// good
let key = `pkg/status/${pId}/${dId}`

使用 template string 取代 string concatenation。

Template String

// bad
let name = 'Sam'
let greeting = `Hello ${ name }`

// good
let name = 'Sam'
let greeting = `Hello ${name}`

{ 與 variable 之間沒有 space。

{ 用於 object 時有 space,用於 string 時沒有 space

Multiple Line

// bad
let 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
let 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
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.`

多行 string 請用 ES6 的 template string。

Statement

if

// bad
if(isDone) {
  
}

// good
if (isDone) {
  
}

if( 之間有 space。

// bad
if ( isDone ) {
  
}

// good
if (isDone) {
  
}

( 與 expression 之間不要有 space。

switch

// bad
switch(expr)
  case 'Orange':
    ...
    break
  case 'Apple'
    ...
    break
  default:
}

// good
switch (expr)
  case 'Orange':
    ...
    break
  case 'Apple'
    ...
    break
  default:
}

switch( 之間有 space。

for

// bad
for(let x of arr)
{
    
}

// good
for (let x of arr) {
  
}

(){ 在同一行。

Math Operator

// bad
let x=y+5

// good
let x = y + 5

Operator 與 operand 之間有 space。

Expression

Mutiple Line Ternary Operator

// bad
let isDone = isPass
  ? true
  : false

// good
let isDone = passed ?
  true :
  false

?: 寫在後面。

Object

Object Literal

// bad
let obj = { name : 'Sam' }

// good
let obj = { name: 'Sam' }

: 左側不加 space,右側加 space。

Property

let obj = { name: 'Sam '}

// bad
obj['name']

// good
obj.name

若 property 名稱不是 variable,使用 . 存取。

let obj = { name: 'Sam' }

let propName = 'name'
obj[propName]

當 property 名稱為 variable 時才使用 []

One Line

// bad
let obj = {name: 'Sam'}

// good
let obj = { name: 'Sam' }

{ 與 property 之間有 space。

Multiple Line

// bad
let obj = 
{
  title: 'FP in JavaScript',
  price: 100
}

// good
let obj = {
  title: 'FP in JavaScript',
  price: 100
}

{= 在同一行。

Method

// bad
let obj = {
  title: 'FP in JavaScript',
  getTitle: function() {
    return this.title
  }
}

// good
let obj = {
  title: 'FP in JavaScript',
  getTitle() {
    return this.title
  }
}

Object 中的 method 使用 ES6 的 property shorthand。

Object Destructuring

// bad
let f = ({title, price}) => `${title}: ${price}` 

// good
let f = ({ title, price }) => `${title}: ${price}` 

{ 與 property 之間有 space。

// bad
let f = ({ title, ,price }) => `${title}: ${price}` 

// good
let f = ({ title, _, price }) => `${title}: ${price}` 

不需要解構的 variable 以 ___ … 表示。

Array

Index

let data = [1, 2, 3]

// bad
let x = data[ 0 ]

// good
let x = data[0]

[ 與 index 之間不要有 space。

Multi Line

// bad
let data = [{
  title: 'FP in JavaScript', price: 100
}, {
  title: 'RxJS in Action', price: 200
}, {
  title: 'Speaking JavaScript', price: 300
}]

// good
let data = [
  { title: 'FP in JavaScript', price: 100 },
  { title: 'RxJS in Action', price: 200 },
  { title: 'Speaking JavaScript', price: 300 }
]

多行時 [{ 不要在同一行。

// bad
let arr = [ 1, 2, 3 ]

// good
let arr = [1, 2, 3]

[ 與 element 之間沒有 space。

Array Destructuring

// bad
let [ from, to ] = getToday()

// good
let [from, to] = getToday()

[ 與 element 之間沒有 space。

Enum

// bad
fetchNotification(0)

// good
let typeEnum = {
  error: 0,
  maintained: 1
}

let fetchNotification(typeEnum.error)

使用 object 模擬 enum,並以 Enum 為 postfix,避免使用 magic number。

Map

// bad
let f = id => {
  if (id === 0) return '異常'
  else if (id === 1) return '維護'
  else return undefined
}

// good
let typeMap = {
  0: '異常',
  1: '維護'
}

typeMap[id]

建立 object 為對照表,並以 Map 為 postfix。

Function

General

// bad
// function declaration
function f1(x, y) {
  return x + y
}

// ok
// function expression
let f2 = function(x, y) {
  return x + y
}

// good
// arrow function
let f3 = (x, y) => x + y

優先使用 arrow function,其次 function expression,不要使用 function declaration。

// bad (error)
let MyBook = title => {
  this.title = title
}

// good
let MyBook = function(title) {
  this.title = title
}

let myBook = new MyBook('FP in JavaScript')

有兩的地方不能使用 arrow function:

  • Vue 要 binding this 時使用 function expression
  • Constructor function 使用 function expression

this 會變動的就不能使用 arrow function

Function Expression

// bad
lef f = function () {
  return 1
}

// good
let f = function() {
  return 1
}

function()() 之間不要有空格。

Argument

// bad
let f = ( x, y, z ) => {}

// good
let f = (x, y, z) => {}

( 與 argument 間不要 space。

Default Argument

// bad
let f = opts => {
  opts = opts || {}
}

// good
let f = (opts = {}) => {}

Default argument 使用 ES6 的 =

Multline Signature

// bad
let f = (x,
         y,
         z) => {}

// good
let f = (
  x,
  y,
  z
) => {}

若 signature 過長要換行,需全部換行。

Multiline Invocation

// bad
console.log(x,
  y,
  z
)

// good
console.log(
  x,
  y,
  z
)

Callback

// bad
[1, 2, 3].map(function(item) {
  return item + 1
})

// good
[1, 2, 3].map(x => x + 1)
  • Callback 使用 arrow function
  • 且 argument 使用 x, y, z
// bad
let f = arr => arr.reduce((a, x) => (x.category !== 'FP') ?[...a, {...x, price: x.price * 0.8 }] : [...a, x], []) 

// good
let f = arr => arr.reduce((a, x) =>
  (x.category !== 'FP') ?
  [...a, {...x, price: x.price * 0.8 }] : 
  [...a, x]
, []) 

只有一行的 arrow function 若過長要加以換行。

Arrow Function

// bad
let f = () => 1

// good
let f = _ => 1

沒 argument 時以 _ 取代 ()

// bad
let f = (x) => x + 1

// good
let f = x => x + 1

單一 argument 時不加 ()

// bad
let f = x => {
  return x + 1
}

// good
let f = x => x + 1

當 body 只有一行且為回傳值時,不要加 {}return

Method Chaining

// bad
[1, 2, 3].filter(x => x % 2 === 1).map(x => x + 1).reduce((a, x) => a += x)

// good
[1, 2, 3]
  .filter(x => x % 2 === 1)
  .map(x => x + 1)
  .reduce((a, x) => a += x)

讓所有 method 的 . 縮排貼齊。

IIFE

// bad
function() { return 1 }()

// good
(function() { return 1 })()
(() => 1)()

IIFE 一律在 anonymous function 加上 ()

Class

// bad
let dt = new Date()

// good
let dt = new Date

當 constructor 不傳 argument 時,() 可省略。

Module

Import / Export Braces

// bad
import {map} from 'ramda'
export {x,y}

// good
import { map } from 'ramda'
export { x, y }

importexport 時,{} 與 variable 間有 space。

// bad
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE,
} from 'path'

// good
import { longNameA, longNameB, longNameC } from 'path'
import { longNameD, longNameE } from 'path'

Reference

airbnb, Airbnb JavaScript Style Guide