---
# System prepended metadata

title: Frontend Coding Style
tags: [Coding Style]

---

###### tags: `Coding Style`
# Frontend Coding Style

## Syntax
![](https://i.imgur.com/WC0P3Nh.png)


## Style
**=== 共同遵守 ===**

> === 取代 == 
> !== 取代 !=

> 變數使用 CamelCase
> 定量使用全大寫+底線
```javascript=
const MINUTES_IN_A_YEAR = 525600
```

> 函式名稱的開頭儘量用動詞 :
> get, set, add, init (initialize 簡寫), calculate, check, ...

> let, const 取代 var: 區塊作用域( block scope ) v.s. 函式作用域( function scope )
> 優先使用 const, 除非變數需要 re-assign
> JS 編譯器會對 const 進行優化，有利於提高程序的運行效率

> 不要使用逗號(,)在同一行來定義(宣告)多個變數或常數, 可讀性較低

> 使用擴展運算符（...）拷貝數組
```javascript=
const itemsCopy = [...items];
```

> Object.assign方法實行的是淺拷貝，而不是深拷貝
```javascript=
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj2.a.b // 2
```

> 避免不必要的 binding

> Function 的參數若太多, 用物件包起來傳進去 (超過3個)
```javascript=
function createMenu({title, body, buttonText, ...}) {}
```

> 函式定義放在使用函式的後面
```javascript=
let elem = createElement();
setHandler(elem);
walkAround();

function createElement() {
  ...
}

function setHandler(elem) {
  ...
}

function walkAround() {
  ...
}
```

> 函數參數的默認值
> 閱讀代碼的人，可以立刻意識到哪些參數是可以省略的
> 參數默認值可以與解構賦值的默認值，結合起來使用
```javascript=
function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined 5
foo({x: 1}) // 1 5
```

> Return early when invalid conditions found
```javascript=
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}
```

> 如果傳入undefined，將觸發該參數等於默認值，null則沒有這個效果
```javascript=
function foo(x = 5, y = 6) {
  console.log(x, y);
}

foo(undefined, null)
// 5 null
```

> 模板字符串
> 如果使用模板字符串表示多行字符串，所有的空格和縮進都會被保留在輸出之中
```javascript=
$('#result').append(`
  There are <b>${basket.count}</b> items
  in your basket, <em>${basket.onSale}</em>
  are on sale!
`);
```
> 模板字符串之中還能調用函數
```javascript=
`foo ${fn()} bar`

function fn() {
  return "Hello World";
}
```

**=== 可討論 ===**

> 字串使用單引號?

> 字尾加分號(;)
> 若不加, (, [, `這些符號不能出現在一行的開頭, 前面要加上(;)
```javascript=
;(function () {
  window.alert('ok')
}())
```

> 單行定義的對象，最後一個成員不以逗號結尾
> 多行定義的對象，最後一個成員以逗號結尾

> 箭頭函式的相關規範
> 箭頭函數中的 this 是定義時的對像, 不是使用時的對像

## Array 
> - forEach
寫法簡單, 但不能中斷循環
```javascript=
myArray.forEach(function (value) {
  console.log(value);
});
```

> - for-of
可以中斷循環(break, continue)
可以循環一個字串
```javascript=
for (let value of myArray) {
  console.log(value);
}
```

> - for-in
不要使用在處理 Array
用的話只針對 Object (可列舉屬性)
```javascript=
const obj = {a:1, b:2, c:3};
    
for (let prop in obj) {
  console.log('obj.' + prop + ' = ' + obj[prop]);
}
```

> - Array.fill
可以接受第二個和第三個參數，用於指定填充的起始位置和結束位置
```javascript=
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
```

> - Array.includes
for Multiple Criteria, 可用於字串
以前使用的 indexOf 方法有兩個缺點:
一是不夠語義化，它的含義是找到參數值的第一個出現位置，所以要去比較是否不等於-1，表達起來不夠直觀
二是，它內部使用嚴格相等運算符（===）進行判斷，這會導致對NaN的誤判
```javascript=
function test(fruit) {
  // extract conditions to array
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}
```
```javascript=
[1, 2, 3].includes(4);	// false
[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true
```
```javascript=
[NaN].indexOf(NaN)
// -1
[NaN].includes(NaN)
// true
```

> - Array.find
回傳第一個符合條件的 Item
```javascript=
[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
});
// 10
```

> - Array.findIndex
回傳第一個符合條件的 Item 的 Index
如果所有成員都不符合條件，則返回-1
```javascript=
[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) 

// ES6
[1, 5, 10, 15].findIndex((value, index, arr) => {
  return value > 9;
}) 
// ES6
[1, 5, 10, 15].findIndex((value, index, arr) => value > 9) 
```

> - Array.map
透過函式內所回傳的值組合成一個陣列
很適合將原始的變數運算後重新組合一個新的陣列
如果不回傳則是 undefined
```javascript=
var officers = [
  { id: 20, name: 'Captain Piett' },
  { id: 24, name: 'General Veers' },
  { id: 56, name: 'Admiral Ozzel' },
  { id: 88, name: 'Commander Jerjerrod' }
];

var officersIds = officers.map(function (officer) {
  return officer.id
});

// ES6
const officersIds = officers.map(officer => officer.id);
```

> - Array.filter
會回傳一個陣列，其條件是 return 後方為 true 的物件
很適合用在搜尋符合條件的資料
```javascript=
var officers = [
  { id: 20, name: 'Captain Piett' },
  { id: 24, name: 'General Veers' },
  { id: 56, name: 'Admiral Ozzel' },
  { id: 88, name: 'Commander Jerjerrod' }
];

var officersIds = officers.filter(function (officer) {
  return officer.id > 50
});

// ES6
const officersIds = officers.map(officer => officer.id > 50);
```

> - 組合範例 for map & filter
```javascript=
const officers = [
  { id: 20, name: 'Captain Piett' },
  { id: 24, name: 'General Veers' },
  { id: 56, name: 'Admiral Ozzel' },
  { id: 88, name: 'Commander Jerjerrod' }
];

// use for
const names = [];
for (const officer of officers) {
  if (officer.id > 50) {
    names.push(officer.name)
  }
}

// use map & filter
const names = officers
  .filter(officer => officer.id > 50)
  .map(officer => officer.name)

```

> - Array.reduce
進行數值加總
```javascript=
// 處理每個元素後等待回傳結果，第一次處理時代入初始值 0
var result = myArr.reduce(function(prev, element) {
    // 與之前的數值加總，回傳後代入下一輪的處理
    return prev + element;
}, 0);

// result: 6

var myArr = [
    'C/C++',
    'JavaScript',
    'Ruby',
    'Java',
    'Objective-C',
    'JavaScript',
    'PHP'
];

// 計算出每種語言出現過幾次
var langStatistics = myArr.reduce(function(langs, langName) {
    if (langs.hasOwnProperty(langName)) {
        langs[langName]++
    } else {
        langs[langName] = 1;
    }
    
    return langs;
}, {});

// langStatistics: { 'C/C++': 1, 'JavaScript': 2, 'Ruby': 1, 'Java': 1, 'Objective-C': 1, 'PHP': 1 }
```

## 物件
> - Object.keys()，Object.values()，Object.entries()
```javascript=
var obj = { foo: 'bar', baz: 42 };

Object.keys(obj)
// ["foo", "baz"]
 
Object.values(obj)
//['bar', 42]

// 不推薦使用
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
```

## 字串
> - includes, startsWith, endsWith
第二個參數，表示開始搜索的位置
```javascript=
const s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('!') // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
```

> - padStart 用於頭部補全, padEnd 用於尾部補全
```javascript=
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(4, 'ab') // 'xaba'
'1'.padStart(10, '0') // "0000000001"
```
