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