写JS一定要优雅!

5/20/2023 js

# 0. 写在前头

这篇文章会不时更新补充,记录我自己平时在学习以及实际开发过程中,总结整理的一些关于JS的优雅写法

鄙人是一个重度代码洁癖患者,能一行代码解决的事,绝对不会书写多行,能代码上下平铺,绝对不会if-if-if多层嵌套

当然了,鄙人码龄不过3年,水平有限,但又喜欢写一些博客记录,属于又菜又爱玩的类型。如有写得不好,写得不对的地方,欢迎各位大佬批评指正!

那么不多bb,进入正题~

# 1. 使用一些位运算取代丑陋的Math方法

# 1.1 取中间值 >>1

针对于正数才有效

let arr = [1,2,3,4,5,6,7]
let l = 0, r = arr.length - 1 

// 获取中间坐标
// let m = Math.floor((l+r)/2)
let m = l + r >> 1

# 1.2 向下取整 | 0

function getDigit(n){ // 获取数字各位上的数(个十百千万)
  let digits = []
  while(n){
    digits.push(n % 10)
    // n = Math.floor(n / 10)
    n = (n / 10) | 0
  }
  return digits
}

# 1.3 判断奇偶 & 1

let n = 9
if(n % 2 == 0) { 
  // 偶... 
} else { 
  // 奇... 
}

// 改进
if( n & 1 ) { ... }// 奇
else { ... } // 偶

# 2.用一行代码取代多行代码

# 2.1 用三目代替if-else

// 是可以,但是不够省流
if(n > 0) {
  fn1()
} else {
  fn2()
}

// 改进
n > 0 ? fn1() : fn2()

// 是可以,但是不够优雅
let n
if (m > 0) {
  n = 1
} else { 
  n = 3
}

// 改进
let n = m > 0 ? 1 : 3

// 逻辑可以,但书写都不太可以,必须要优雅
async function saveForm() {
  if (isCreate) { // 创建
    let params = {
      user: "noxone",
      age: 17,
      isHandsome: true
    }
    let res = await apiCreateUser(params)
    if (res.code == "0") alert("创建成功")
    else alert(res.message)
  } else { // 编辑
    let params = {
      id: 295286392
      user: "noxone",
      age: 17,
      isHandsome: true
    }
    let res = await apiEditUser(params)
    if (res.code == "0") alert("编辑成功")
    else alert(res.message)
  }
}
// 改进
async function saveForm(isCreate) {
  let params = {
    user: "noxone",
    age: 17,
    isHandsome: true
  }
  let { code, message } = isCreate ? 
    await apiCreateUser(params) 
    : await apiEditUser({id: 295286392, ...params})
    
  if (code !== "0") alert(message)
  else alert(isCreate ? "创建成功" : "编辑成功")
}

# 2.2 用与逻辑代替if-fn()

if (n > 2) {
  fn()
}

// 省字高效
n > 2 && fn()

# 2.3 用for代替while

function reverse(arr) {
  let l = 0, r = arr.length - 1
  while (l < r) {
     [arr[l++], arr[r--]] = [arr[r], arr[l]]
  }
}

// for
function reverse(arr) {
  for (let l = 0, r = arr.length - 1; l < r;){
    [arr[l++], arr[r--]] = [arr[r], arr[l]]
  }
}

# 2.4 利用好布尔值

// 以下代码看着费劲
function fn(n) {
  let flag
  if (n > 5) {
    flag = true
  } else if(n % 2 == 0) { // 偶数
    flag = true
  } else {
    flag = false
  }
  return flag
}
// 改进 
function fn(n) {
  return n > 5 || n % 2 == 0
}
// 以下代码看着也费劲
function fn(num, str, obj, arr) {
  if (num == 0) {
    return false
  }
  if (str == '') {
    return false
  }
  if (obj == null) {
    return false
  }
  if (arr.length == 0) {
    return false
  }
  if (arr.indexOf(1) != -1 || arr.indexOf(2) != -1 || arr.indexOf(3) != -1) {
    return false
  }
  
  return true
}

// 改进:利用好js的假值及自动类型转换特性
function fn(num, str, obj, arr) {
  return !!n && !!str && !!obj && !!arr.length && arr.some(n=>[1,2,3].includes(n)) 
}

同上

var n = 5
if (n != 0) {
  // ...
} else { 
  // ...
}

// 改成这样
var n = 5
if (n) {
  // ...
} else {
  // ...
}

// 同理
var arr = []
var str = 'abc'
var obj = null

if (!arr.length) {
  // 空数组
} else {
  // 非空
}

if (!str) {
  // 空字符串
}

if (obj) {
  // 非null
} else {
  // null
}

# 3. 降低横向嵌套

# 3.1 降低if-if-if多层套娃

以下代码是4层if-else嵌套的代码,即使已经将具体逻辑封装到一个个函数里边了,但是还是看着费劲,更别说未封装的情况!

理论上大于3层的代码都不好理解,且维护困难,容易出问题!

// holy shit!!!
function fn(data) {
  if (data !== null) {
    if (data.num >= 5) {
      fn2()
      if (data.flag) { // 是否可以data.flag && data.isAdmi呢?
        if(data.isAdmin) {
          fn3()
        }
      } else{
        fn3Err()
      }
    } else if (data.num >= 0) {
      fnErr2()
    } else {
      fnErr3()
    }
  } else {
    fnErr()
  }
}

这时候需要把横向嵌套的层数降低,代码书写方式尽量是自上而下,而非上左左下

改进方法:先处理坏情况,剩下的就是好情况,从而把横向嵌套层数降为1

function fn(data) {
  if (data == null) return fnErr() // 1级坏情况:data为null
  if (data.num < 0) return fnErr2() // 2级坏情况
  if (data.num < 5) return fnErr3() // 2级坏情况
  
  // 剩下的就是1级好情况了,即data !== null && data.num >= 5
  fn2()
  if (!data.flag && data.isAdmin) return fn3Err() // 3级坏情况
  fn3()
  // 或合并成一行: data.flag && data.isAdmin ? fn3() : fn3Err()
}

这样一看是不是豁然开朗多了?

原理就是:把if (isTrue) { ... }改成if(!isTrue) return实现优雅减低嵌套

// 这里用fn1、fn2、fn3来代替一些复杂的业务逻辑代码
if (isTrue1) { // 一层嵌套
  fn1()
  if (isTrue2) { // 二层嵌套
    fn2()
    if (isTrue3) { // 三层嵌套
      fn3()
    }
  }
}

// 优雅降低嵌套
if (!isTrue1) return 
fn1()
if(!isTrue2) return
fn2()
if(!isTrue3) return
fn3()

# 3.2 降低循环里的if-if-if套娃

平时写for循环代码如下

// 举个例子,考虑以下场景:
// 将大于10且为偶数的数字放入ret数组
let ret = []

let data = [1,11,5,8,15,12,14,7,2]
for (let n of data) {
  if (n & 1) { // 偶数
    if (n > 10) {
      ret.push(n)
    }
  }
}

优雅降级

for (let n of data) {
  if(!(n & 1) || n <= 10) continue // 是奇数或者不大于10就continue跳过
  ret.push(n)
}
// 当然你也可以这样:ret = data.map(n =>(n & 1) && n > 10)

如果再加一个条件:偶数但不大于10放入ret2数组

let data = [1,11,5,8,15,12,14,7,2]

for (let n of data) {
  if(!(n & 1)) continue // 非偶数直接跳过
  
  if(n > 10) { // 偶数大于10
    ret.push(n)
    continue
  }
  // 剩下的情况当然是偶数且不大于10啦~
  ret2.push(n)
  
  // 或者使用三元合并成一行: n > 10 ? ret.push(n) : ret2.push(n)
}

X### 3.3 放弃丑陋的Promise.thenPromise.then代码不太直观,而且太多的Promise.then链式嵌套会让代码看起来又臭又长,既然ES6之后推出了async来替代Promise`

以下是使用Promise.then 写的死亡三角代码,自己品品吧~

// 现在我要获取用户及用户配偶的信息
let userInfo = { uid: 1 }
let userSpouse = {}

function getUserInfo(){
  apiRequest1(userInfo.uid).then((data1)=>{ // 通过uid获取用户的基本详细
    userInfo = { ...userInfo, ...data1 } // data1包含用户的家庭id
    apiRequest2(userInfo.homeId).then((data2)=>{ // 通过用户的家庭id获取家庭基本信息
      userInfo = { ...userInfo, ...data2 } // data2包含用户的配偶id
      apiRequest1(userInfo.spouseId).then((data3)=>{ // 通过用户配偶id获取用户配偶信息
        userSpouse = { ...userSpouse, ...data3 }
        apiRequest2(userSpouse.homeId).then((data4)=>{ // 再获取家庭基本信息
          userSpouse = { ...userSpouse, ...data4}
        }).catch(e=>console.error(e))
      }).catch(e=>console.error(e))
    }).catch(e=>console.error(e))
  }).catch(e=>console.error(e))
}

而改成async明显舒适多了,可阅读性立马提升!

async function getUserInfo() {
  try {
    let data1 = await apiRequest1(userInfo.uid)
    let data2 = await apiRequest2(data2.homeId)
    userInfo = { ...userInfo, ...data1, ...data2 }
    let data3 = await apiRequest1(userInfo.spouseId)
    let data4 = await apiRequest2(data3.homeId)
    userSpouse = { ...userSpouse, ...data3, ...data4 }
    } catch (e) {
      console.error(e)
    }
}
    一定要爱着点什么,
    恰似草木对光阴的钟情。
    红莲华
    x
    loading...