1. 程式人生 > >編寫更優雅的 JavaScript 程式碼

編寫更優雅的 JavaScript 程式碼

總結下對 JavaScript 程式碼編寫的認識,寫儘可能優雅的程式碼。

程式碼技巧

  1. ES2015+ 新特性寫法

熟練使用 ES6 新特性可以優化程式碼,更加簡潔,程式碼對比

// 箭頭函式
function foo(){
  console.log('hello world')
}

const foo = () => console.log('hello world')

// 陣列去重
const formatArray = arr => [...new Set(arr)]

// 數組合並
const newArr = [...arr1, ...arr2, 'value']

// 物件淺拷貝
const newObj = {...obj}

// 解構賦值
const person = {name: 'bao', age: 18}
const { name, age } = person

// 常見物件屬性取值前判斷物件是否存在
// 如下是 react + antd 渲染 table 的簡單程式碼,物件存在性判斷 + 預設值設定

render(){
  const { downloadList } = this.props.store.downloadList
  let items = downloadList && downloadList.items || []
  let itemCount = downloadList && downloadList.itemCount || 10
  
  return <Table dataSource={items} pagination={{total: itemCount}} />
}
// 優化後
render(){
  const { items, itemCount } = this.props.manageStore.downloadList || {}
  return <Table dataSource={items || []} pagination={{total: itemCount || 10}}/>
}
  1. 優化邏輯判斷語句

大量的 if else 邏輯判斷難以維護,且效能較差,可用多種方式代替

// 物件配置法
// 函式內部有條件判斷,且 return 值時,滿足條件立即return,而不要在結尾return
const foo = v => {
  if (v === 'name') {
    return 'bao'
  } else if (v === 'age') {
    return '18'
  } else if (v === 'height') {
    return '180'
  }
}
const cfg = {
  name: 'bao',
  age: '18',
  height: '180'
}
const foo = v => cfg[v]

// 陣列配置法
if (value === 'hello' || value === 'world' || value === 'blabla') {
  // ...
}
// 配置陣列形式
const rightValue = ['hello', 'world', 'blabla']
if (rightValue.includes[value]) {
  // ...
}
  1. 善用 && 、 || 和 三元運算
if (name === 'bao') {
  someFunc()
}

name === 'bao' && someFunc()

if (name === 'bao') {
  someFunc()
} else {
  elseFunc()
}

name === 'bao' ? someFunc() : elseFunc()
  1. 物件屬性變數應用

如在 react 中,呼叫 action 方法來獲取資料,不同條件執行不同方法

if (isMember) {
  let res = await actions.getMemberInfo(params)
} else {
  let res = await actions.getCommonUserInfo(params)
}

const actionName = isMember ? 'getMemberInfo' : 'getCommonUserInfo'
let res = await actions[actionName](params)
  1. 型別轉換
// 字串轉數字
let str = '1234'
let num = +str

console.log(+new Date()) // 1536231682006

// 轉字串
let str = `${num}`
  1. 用 Array.map(), Array.filter() 代替陣列 for 迴圈實現簡易寫法

如下對陣列元素的操作

let arr = [1, 2, 3, 4, 'A', 'B']

// 1. 取出 arr 中數字項為新陣列
let numArr = []
for(let i in arr){
  if(typeof arr[i] === 'number'){
    numArr.push(arr[i])
  }
}

// 改用filter
let numArr2 = arr.filter(item => typeof item === 'number')
console.log(numArr2) // [1,2,3,4]

// 2. 獲得新陣列,元素是 arr 每個元素作為 value, key 為 arr 下標的物件, 不修改 arr
let strArr = []
for(let i in arr){
  strArr.push({[i]: arr[i]})
}
// 改用 map
let strArr2 = arr.map((item, i) => ({[i]: arr[i]}))
console.log(strArr2) // [ { '0': 1 },{ '1': 2 },{ '2': 3 }, { '3': 4 }, { '4': 'A' }, { '5': 'B' } ]
  1. 淺拷貝、深拷貝 複雜資料型別物件深拷貝建議使用庫來實現,如 lodash.cloneDeep
// 淺拷貝
let obj1 = { a: 11, b: { ba: 22 } }
let obj2 = {...obj1}
console.log(obj2); // ​​​​​{ a: 11, b: { ba: 22 } }​​​​​

console.log(obj1 === obj2); // false
console.log(obj2.b === obj1.b); // true

// 深拷貝,這種方法需要物件能夠被 json 序列化
let obj3 = JSON.parse(JSON.stringify(obj1))
console.log(obj3); //  ​​​​​{ a: 11, b: { ba: 22 } }​​​​​
console.log(obj3===obj1); // false
console.log(obj3.b===obj1.b); // true
  1. optional-chaining ?. 代替物件是否存在判斷

需要新增 @babel/plugin-proposal-optional-chaining 外掛支援,可參考babel 7 簡單升級指南

// obj?.prop       // optional static property access
// obj?.[expr]     // optional dynamic property access
// func?.(...args) // optional function or method call

// 如下程式碼
let { data } = this.props
let list = data && data.tableData && data.tableData.list || []

// 使用 ?.
let list = data?.tableData?.list || []

更高效的程式碼

  1. 使用區域性變數代替引用型別查詢

區域性變數的讀取速度最快,而引用型別的資料讀取需要按引用指標去查詢,所以可以對多次使用的引用型別屬性 使用區域性變數讀取一次,重複使用

let obj = {
  person: {
    man: {
      bao: {
        age: 18
      }
    }
  }
}

let age = obj.person.man.bao.age
// use age do many things
  1. 刪除多個物件屬性時先使屬性為 null

刪除屬性時,js 引擎會去查詢該屬性的值是否是其他物件的引用,所以刪除前提前賦值為 null,可以減少 js 引擎的檢測過程,提高效率

let obj = {
  person: {
    man: {
      bao: {
        age: 18
      }
    }
  }
}
obj.person = null
delete obj.person
  1. 區域性變數儲存陣列 length
    關於這個優化的討論一直不少,參考有沒有必要快取JavaScript陣列的length
let len = arr.length
for(let i=0; i<len; i++)){
  // ...
}

程式碼結構組織等優化

隨著專案(react 專案)的日益擴大,程式碼量快速增多,後期維護非常耗費時間,主要通過以下方法減少程式碼量

  1. 凡是二次出現的程式碼片段,立即考慮複用,拆分公共元件
  2. 封裝功能函式

最近做了一個複雜的圖表、表格資料互動功能,涉及表格資料的各種計算、排序、比較、編輯前後對比等功能, 邏輯複雜,按互動不同有多種邏輯執行過程。採取的方法是單一功能函式封裝,對每個計算、資料處理步驟, 都封裝為小函式,不同邏輯不同函式元件組合,保證每個函式的可用性,整體功能也可以保證質量。

同時可以將專案常用的功能方法封裝公用 util 來複用使用

  1. 善用 class 類的繼承,複用功能方法 主要封裝了 fetch 請求的 get/post 方法到 CommonActions class, 然後建立 actions 時只需要 extends CommonActions 就可以在新的 actions 中直接呼叫 this.get()/this.post() 來完成 請求資料。

總之,在邏輯清晰的情況下,儘可能複用元件、方法,維護好高質量的公共元件、方法,便於專案維護

  1. 善用裝飾器 react 高階函式已經提供了一種優雅的元件複用形式,而裝飾器的使用可以更優雅地實現高階函式 如我的另一篇記錄 React Error Boundary 元件異常處理方案實踐

同時,ant design Form 元件的建立也可用裝飾器更簡單的實現

class MyForm extends React.Component {
  // ...
}

const WrapForm = Form.create()(MyForm)

// 其他元件使用 WrapForm 元件
// 裝飾器形式

@Form.create()
class MyForm extends React.Component {
  // ...
}

編不下去了,暫時就想起來這麼多,想起來了再新增…

轉自:https://juejin.im/post/5b8fd36fe51d450e6475a92d