iT邦幫忙

2022 iThome 鐵人賽

DAY 28
3

前言

終於到了第 28 天的鐵人賽,Day28, 29 我將會分享一些寫 JS 的一些技巧,讓我們寫出的 JS 程式碼品質更好。

不過在開始介紹之前也先分享一篇不錯的長文: 無瑕的程式碼 JavaScript,讀者可以閱讀看看。


條件判斷

避免使用超多 if else

在做很多狀態的判斷時,還在寫超級多個 if else 嗎?
這裡有一些技巧,在做簡單的單一條件判斷時,可以宣告一個物件,把判斷的條件設定為 key,判斷的結果為 value,或是用 Map 物件也可以。

以下我會將這個範例做一個重構,讓程式碼變簡潔也不失可讀性。

let userStatus = '';
let theme = '';

function changeStatusByBMI(BMI) {
  if (BMI > 40) {
    userStatus = '重度肥胖';
    theme = 'danger';
  } else if (35 < BMI && BMI <= 40) {
    userStatus = '中度肥胖';
    theme = 'middanger';
  } else if (30 < BMI && BMI <= 35) {
    userStatus = '輕度肥胖';
    theme = 'lightendanger';
  } else if (25 < BMI && BMI <= 30) {
    userStatus = '過重';
    theme = 'overweight';
  } else if (18.5 < BMI && BMI <= 25) {
    userStatus = '理想';
    theme = 'normal';
  } else if (BMI <= 18.5) {
    userStatus = '過輕';
    theme = 'overlighten';
  }
}

changeStatusByBMI(23);
console.log(userStatus);

以下是重構 changeStatusByBMI 函式後的結果。我用 Map 去儲存判斷的條件,並轉成一個函式當作 key,而要設定給兩個變數的值則是存成陣列當 value。

像這樣透過物件或是 Map 去儲存的方式,在判斷大量條件時將能達到更簡潔的效果。

function changeStatusByBMI(BMI) {
  const BMIConditions = new Map([
    [(value) => value > 40, ['重度肥胖', 'danger']],
    [(value) => 40 >= value && value > 35, ['中度肥胖', 'middanger']],
    [(value) => 35 >= value && value > 30, ['輕度肥胖', 'lightendanger']],
    [(value) => 30 >= value && value > 25, ['過重', 'overweight']],
    [(value) => 25 >= value && value > 18.5, ['理想', 'normal']],
    [(value) => 18.5 >= value, ['過輕', 'overlighten']],
  ])
  
  BMIConditions.forEach((value, checkFunc) => {
    if (checkFunc(BMI)) {
      userStatus = value[0];
      theme = value[1];
    }
  })
}

減少巢狀

其實蠻多時候我們是沒有必要多寫 if 區塊和 else 區塊的喔,以下來舉幾個例子。

範例 1

for 迴圈內,想跳過不符合條件的狀況,可以用 continue 去跳過。

原本版:

for (let i = 0; i < 10; i++) {
  if (condition) {
    // ...
  }
}

改良版:

for (let i = 0; i < 10; i++) {
  if (!condition) continue;

  // 少了一層巢狀...
}

範例 2

某些情況下可以提早做 return 的動作,以下範例的巢狀判斷就有些多餘,來重構一下。

原本版:

function addTwoPositiveNums(a, b) {
  if ((typeof a !== 'number') || (typeof b !== 'number')) {
    throw new Error("Other type parameters is not supported");
  } else {
    if (a < 0 || b < 0) {
      throw new Error("Negative number is not supported");
    } else {
      return a + b;
    }
  }
}

console.log(addTwoPositiveNums(1, 'hi')); // Other type parameters is not supported
console.log(addTwoPositiveNums(-1, 1)); // Negative number is not supported
console.log(addTwoPositiveNums(1, 1)); // 2

改良版,看起來易讀多了:

function addTwoPositiveNums(a, b) {
  if ((typeof a !== 'number') || (typeof b !== 'number')) {
    throw new Error("Other type parameters is not supported");
  }
  
  if (a < 0 || b < 0) throw new Error("Negative number is not supported");
  
  return a + b;
}

將一些邏輯抽出成函式

上個範例中,if 括號內的判斷式還算簡單,如果複雜一點我會抽成一個函式,透過函式的名稱去說明這段判斷式在做什麼事。

原本版:

function findPrimesFromZeroToNum(num) {
  const primes = [];
  for (let i = 2; i < num; i++) {
    let isPrime = true;
    for (let j = 2; j < i; j++) {
      if (i % j === 0) isPrime = false;
    }
    if (isPrime) primes.push(i);
  }
  return primes;
}

console.log(findPrimesFromZeroToNum(30)); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

改良版:

function findPrimesFromZeroToNum(num) {
  const primes = [];
  for (let i = 2; i < num; i++) {
    if (isPrime(i)) primes.push(i);
  }
  return primes;
}

// 抽出判斷質數的邏輯
function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if (n % i === 0) return false;
  }
  return true;
}

解構應用

範例 1

跳過第二個陣列元素進行解構。

const [one, , two] = ["A", "B", "C", "D"];

console.log(two); // C

範例 2

兩個值交換。

let guest = "Jack";
let admin = "Harry";

[guest, admin] = [admin, guest];

console.log(`${guest} ${admin}`); // Harry Jack

範例 3

巢狀物件解構。

const person = {
  name: {
    first: 'Harry',
    last: 'Xie'
  }
}

// 解構再解構
const { name: { first, last } } = person;

// 解構再解構 + 重新命名
const { name: { first: firstName, last: lastName } } = person;
 
console.log(first); // 'Harry'
console.log(last); // 'Xie'

// 解構的兩層物件都會用到 ex: data 和 lines
// const { data: { lines }, data } = await callSomeApiExample();

範例 4

物件、陣列混合的多層解構。

const userData = {
  name: 'Harry',
  figure: {
    height: 172,
    weight: 65
  },
  hobbies: ['coding', 'comic', 'workout'],
};


const {
  name,
  figure: {
    height,
    weight
  },
  hobbies: [hobby1, hobby2, hobby3],
  job = 'software enginner'
} = userData;

console.log(name);  // Menu
console.log(height);  // 100
console.log(weight); // 200
console.log(hobby1);  // Cake
console.log(job);  // Donut

範例5.

運用解構複製物件並移除指定的屬性。

const oldObj = { a: 1, b: 2, c: 3 };
const { a, ...newObj } = oldObj;
console.log(oldObj);  // { a: 1, b: 2, c: 3 }
console.log(newObj);  // { b: 2, c: 3 }

善用高階函式

這裡舉個範例,sort 函式本身就是高階函式了,那接下來傳入它的參數函式也把它寫成一個高階函式,透過這樣的方式我們可以根據要排序的物件屬性去做排序。

const arr = [
  { name: 'John', age: 92 },
  { name: 'Dave', age: 42 },
  { name: 'Justin', age: 3 }
]

const propComparator = (propName) =>
  (a, b) => a[propName] == b[propName] ? 0 : a[propName] < b[propName] ? -1 : 1

arr.sort(propComparator('name'))
console.log("By name", arr)

arr.sort(propComparator('age'))
console.log("By age", arr)

這篇就介紹到這裡,有可能未來發現更多技巧還會陸續補充(?


參考文章 & 推薦閱讀

JavaScript 复杂判断的更优雅写法

Destructuring assignment


上一篇
Day27-JavaScript 的型別轉換 / == 和 === 和 Object.is() 的比較
下一篇
Day29-寫出更好的 JavaScript 程式碼(下)
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言