終於到了第 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 區塊的喔,以下來舉幾個例子。
for 迴圈內,想跳過不符合條件的狀況,可以用 continue 去跳過。
原本版:
for (let i = 0; i < 10; i++) {
if (condition) {
// ...
}
}
改良版:
for (let i = 0; i < 10; i++) {
if (!condition) continue;
// 少了一層巢狀...
}
某些情況下可以提早做 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;
}
跳過第二個陣列元素進行解構。
const [one, , two] = ["A", "B", "C", "D"];
console.log(two); // C
兩個值交換。
let guest = "Jack";
let admin = "Harry";
[guest, admin] = [admin, guest];
console.log(`${guest} ${admin}`); // Harry Jack
巢狀物件解構。
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();
物件、陣列混合的多層解構。
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
運用解構複製物件並移除指定的屬性。
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)
這篇就介紹到這裡,有可能未來發現更多技巧還會陸續補充(?