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

## 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"
```