Sitemap
Hannah Lin

Love coding ❤

Follow publication

Clean Code for condition and loop

10 min readOct 22, 2024

--

Clean Code 系列文

1. 成為資深前端工程師的第一步 JavaScript Best Practice
2. Clean Code for condition and loop

最近面試碰到拿出一段 dirty code,叫求職者 refactor 的機率實在太高,決定好好來筆記一下 clean code 的 tips。

此篇並不會詳細說明 Clean Code 的 SOLID 原則,而是偏向面試那樣,如何改寫 code 才能更簡單明瞭好維護,網路上已有許多超棒資源 clean-code-javascript,所以本篇只會著重在最煩人的 condition (if, else) 跟 loop (for)。

🔖 文章索引

Condition
1. Guard Clause
2. 善用 return

Loop
3. 用 for of 取代 for
4. break & continue
5. Use forEach, map, filter...

實務題1

Condition

Guard Clause

Check if the remaining lines of code should be executed or not

一般 if else 都要一個一個檢查直到找到符合條件結果,但 Guard Clause 就像有一個守衛站在門口,直接告訴你結果,可以大大縮減程式碼

// 👎 Bad
const checkAuth = () => {
if(userIsAuth){
if(userIsAdmin){
showAdminDashboard()
} else {
showHomePage()
}
} else {
redirectTOLoginPage()
}
}
// ✅ Good
const checkAuth = () => {
if(!userIsAuth) return redirectTOLoginPage()
if(userIsAdmin) return showAdminDashboard
showHomePage()
}

通常會先優先處理 fail 的情況

// 👎 Bad
function foo(callback) {
if (success) {
callback()
} else {
// handle error
}
}

// ✅ Good
function foo( callback) {
if (!success) {
return err
}
callback()
}

善用 return

function 一碰到 return 就會跳出函式不會繼續執行 return 之後的程式碼,所以可以好好善用這個特性讓 condition 變短

// 👎 Bad
function getStageOfLifeByAge = age => {
let stageOfLife = ''
if(age < 1){
stageOfLife = 'Infancy'
} else if(age >=1 && age < 4){
stageOfLife = 'Todderhood'
} else if(age >=4 && age < 10){
stageOfLife = 'ChildHood'
} else if(age >=10 && age < 21){
stageOfLife = 'Adolescence'
} else if(age >= 21){
stageOfLife = 'Adulthood'
}
return stageOfLife
}

// ✅ Good
function getStageOfLifeByAge = age => {
if(age < 1) return 'Infancy'
if(age < 4) return 'Todderhood'
if(age < 10) return 'ChildHood'
if(age < 21) return'Adolescence'
return 'Adulthood'
}

Loop

用 for of 取代 for

當不需要 index 時,for of 會是比較簡潔做法

const users = [
{ firstName: 'Hannah', lastName: 'Lin'},
{ firstName: 'Timo', lastName: 'Lin'},
]
// 👎 Bad
for(let i=0; i<users.length; i++){
console.log(`${users[i].firstName} ${users[i].lastName}`)
}

// ✅ Good
for(let user of users){
console.log(`${user.firstName} ${user.lastName}`)
}

// Hannah Lin
// Timo Lin

也可以搭配 destructuring

const user = { firstName: 'Hannah', lastName: 'Lin' }

for(let [firstName, lastName] of Object.entries(user)){
console.log(`${firstName}: ${lastName}`)
}

// firstName: Hannah
// lastName: Lin

break & continue

break let you quit the loop early;
continue jump out of the current cycle and keep run to next cycle.

for(let i=0; i<5;i++){
if(i===3) break
console.log(i)
}
// 0 1 2

for(let i=0; i<5;i++){
if(i===3) continue
console.log(i)
}
// 0 1 2 4

好好善用它們可以減少多餘判斷式 (flag control),也可以提升效能 (減少 loop 運算次數,不需要每次都跑完)

const users = ['Hannah Lin', 'Timo Lin']
// 👎 Bad 總共算了 users.length 次
let found = false
for(let user of users){
if(!found){
if(user === 'Hannah Lin')
console.log(`Found ${user}`)
found = true
}
}

// ✅ Good 只算了 1 次
for(let user of users){
if(user === 'Hannah Lin') break
}

// 也可以結合 Guard Clause
for(let user of users){
if(user !== 'Hannah Lin') continue
console.log(`Found ${user}`)
break
}

Use React native array method (like map, filter, reduce…)

使用他們好處是因為他們都接受一個 callback function,所以是比較容易實現 reuse 的

以上方法大家應該都很熟悉,唯一要注意的是盡量使用 immutable 的 method,在查找過程中發現 js 已經把很多原本 mutable 方法新增成 immutable 了,例如 toSorted(), toReversed(), toSpliced() ,用法還跟本來的一模一樣可以無痛轉移! (但支援度要注意)

實務題 1

以下是真實在面試中的考題,都是問哪邊可以寫過好,當然還包括 React 部分

// 改寫前
function Item({ index, listData, itemData, setListData }) {
const handleClick = () => {
listData[index] = `${listData[index]} - click`;
setListData(listData);
};
return <div onClick={handleCLick}>{itemData.name}</div>;
}

export default function App() {
const [listData, setListData] = useState([
{ name: "Apple" },
{ name: "Banana" },
{ name: "Kiwi" },
]);

return (
<>
<div className="App">
{listData?.map((itemData, idx) => (
<>
<Item
key={idx}
index={idx}
itemData={itemData}
listData={listData}
setListData={setListData}
/>
</>
))}
</div>
</>
);
}
  • 最嚴重就是 Item component click function 根本不 work,因為 Object 的 reference 根本沒變所以 React 會覺得他是同一個東西所以不會 re-render (延伸閱讀: 為什麼更新 React 中的 state 要用 immutable 的寫法? )
  • 可以把 listData[index] = `${listData[index]} — click` 拉出來寫成獨立 function
  • 可以把 handleClick 拉在 parent 統一管理就不需要在拿這麼多參數下來
  • 避免用 index 作為 key: key 建議不要用 idx,而是在 listData 新增 id 屬性 (延伸閱讀 : 為什麼 React 渲染列表時需要加上 key?)
// 改寫後
function Item({ handleClick, itemData }) {
return <div onClick={handleClick}>{itemData.name}</div>;
}

export default function App() {
const [listData, setListData] = useState([
{ name: "Apple", id: 1 },
{ name: "Banana", id: 2 },
{ name: "Kiwi", id: 3 },
]);

const handleClick = (index: number) => {
let newList = listData.map((itemData, idx) =>
idx === index
? { ...itemData, name: `${itemData.name} - click` }
: itemData
);

setListData(newList);
};

return (
<>
<div className="App">
{listData?.map((itemData, idx) => (
<>
<Item
key={idx}
itemData={itemData}
handleClick={() => handleClick(idx)}
/>
</>
))}
</div>
</>
);
}

有其他好的改進方法也歡迎留言喔,畢竟這都是開方式回答

--

--

Hannah Lin
Hannah Lin

Written by Hannah Lin

A Front End Developer girl comes from Taiwan

No responses yet