0%

儘早 Return 函數:讓 if 邏輯條件判斷不再一層又一層!

程式新手只會知道 function 回傳值的時候需要用到 return,但 return 的使用時機只有如此嗎?一起來看看如何用 return 讓程式碼變得更簡潔易懂的方法吧!


HIHI~😍 如果你是第一次來的話,『Chan-Chan-Dev』是一個專門用簡單的圖文與故事講解網路程式技術的網站。
若你也喜歡用這種方式學習的話,歡迎加入 Chan-Chan-Dev Facebook 的粉絲團,在發佈的時候就有比較多機會收到通知喔!😍


Cover
相信大家在寫程式的過程中,常常會遇到一些需要判斷好幾個不同狀況的時候,例如:表單送出前的驗證。

例如以下有一個類似這樣子的表單:

Simple-Form

上面的欄位設定都是爲必須填寫的,所以在讓使用者送出之前,我們就必須要一一地檢查使用者是不是有輸入內容(這邊討論的是不使用套件來做驗證的範圍喔 😆)。

所以我們要檢查以下:

  • 使用者帳號是否有填寫,如果沒有就跳出提示。
  • 使用者 Email 是否有填寫,如果沒有就跳出提示。
  • 使用者 Email 格式是否正確,如果沒有就跳出提示。
  • 使用者密碼是否有填寫,如果沒有就跳出提示。
  • 使用者地址是否有填寫,如果沒有就跳出提示。
  • 使用者聯絡電話是否有填寫,如果沒有就跳出提示。

我們可能會先畫出這樣子的判斷圖

Logic Diagram

於是乎我們很自然而然就會寫出大概以下的判斷:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"
defer
></script>
<style>
* {
box-sizing: border-box;
}

.main {
background: rgb(255, 240, 242);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}

form {
min-width: 400px;
background: rgb(255, 149, 149);
padding: 20px;
border-radius: 10px;
box-shadow: 5px 10px rgba(255, 192, 203, 0.692);
}

input,
label {
display: block;
width: 100%;
margin-bottom: 10px;
text-align: center;
color: #444;
}

input {
padding: 8px;
border-radius: 5px;
border: 1px solid #ddd;
}

button {
border: none;
background: rgb(255, 210, 63);
padding: 10px;
border-radius: 10px;
width: 100%;
font-size: 28px;
color: #666;
color: rgb(53, 53, 53);
}
</style>
</head>
<body>
<div class="main">
<form x-data="formData()">
<label>
UserName
<input type="text" x-model="username" />
</label>
<label>
Email
<input type="email" x-model="email" />
</label>
<label>
Password
<input type="password" x-model="pw" />
</label>
<label>
Address
<input type="text" x-model="address" />
</label>
<label>
Phone
<input type="text" x-model="phone" />
</label>
<button type="submit" @click.prevent="submit">Submit</button>
</form>
</div>
<script>
function formData() {
return {
username: '',
email: '',
pw: '',
address: '',
phone: '',
submit() {
if (this.username !== '') {
if (this.email !== '') {
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email)) {
if (this.pw !== '') {
if (this.address !== '') {
if (this.phone !== '') {
alert('成功送出囉!');
document.forms[0].submit();
} else {
alert('請輸入聯絡電話,謝謝。');
}
} else {
alert('請輸入地址,謝謝。');
}
} else {
alert('請輸入密碼,謝謝。');
}
} else {
alert('請輸入 Email 的正確格式,謝謝。');
}
} else {
alert('請輸入 Email,謝謝。');
}
} else {
alert('請輸入帳號,謝謝。');
}
},
};
}
</script>
</body>
</html>

以上的程式碼應該可以達到簡易驗證輸入內容的功能吧 😎

if 的階層數太多的缺點

已經可以完成功能就已經好棒棒囉,但是上述的程式碼還是有一些可以調整的空間:

if 的階層數太多,可能會導致程式碼上的可讀性降低。

例如:如果已經看到了 /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email) 就不太容易找到相對應的 else 區塊在下方的那一段了。

if 的階層數太多,要思考的邏輯也跟著變多。

例如:一開始是判斷 this.username !== '' 馬上又接著判斷 this.email !== '' ,其後馬上又接著 /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email) ,所以腦袋可能需要存放這樣子的『邏輯理解暫時記憶』:『有填寫使用者帳號且有填寫 Email 且 Email 的格式正確且 …..』,需要一路看到最後的 this.phone !== '' ,腦袋已經用且接了好幾個不同的判斷邏輯了,一個閃神很可能就會不小心發生思考錯誤的狀況。

if 的階層數太多,當看到其中一層的時候就需要往上去看一層的條件是什麼,所以當自己每多寫一層自己就要從第一層 if 重新地順到目前寫的 if 階層,判斷邏輯是否有錯誤。

例如:假如現在需要再多一個判斷電話的格式是否正確,那我們就會在 this.phone !== '' 的判斷多寫一個 if 來判斷電話格式,所以當寫完的時候我們自己就需要從第一層的使用者帳號的邏輯判斷一路順到新增加的電話格式判斷

一切都是 if 的階層數太多所造成上述問題的根本原因!因此減少 if 的階層數就是我們的首要目標了 🤩

---

💪 儘早 return 函數

在 function 裏面如果有回傳值,我們會用 return 的關鍵字並且在其後加上需要回傳的內容。然而 return 的功能除了在程式有需要的回傳的情況下回傳結果以外,它同時還會有另外一個功能:

中止函數的執行

例如:

1
2
3
4
5
6
7
8
function myFn() {
console.log('hihi');
return;
console.log('My');
console.log('Name');
console.log('Is');
console.log('JC');
}

這一段程式碼運行後,應該就只會看到 hihi 出現在 console 裏面,後面剩下的都不會出現,因爲在 console.log('hihi'); 之後就馬上 return 中止函數的執行了,自然不會印出 return 下方的內容囉。

既然我們知道 return 可以用來『中止函數

那我們是不是就可以換個方向思考,用 return 來中止已經確定爲失敗的 case 呢?

所以如果是一開始的 this.username !== '' ,我們先處理 username 沒有填寫的話的處理方式,所以如果程式執行的時候有掉入這個條件判斷裡,我們就可以在跳出提醒通知後,就可以用 return 中止函數了,因爲在往下執行似乎也沒有幫助,畢竟就是沒有填寫 username 的內容呀。

1
2
3
4
5
6
7
8
9
10
11
12
// 先判斷沒有填寫帳號的狀況
if (this.username === '') {
alert('請輸入帳號,謝謝。');
return;
}

// 若程式能夠執行到這一行,代表一定有填寫帳號
// 否則就會在上個 if 被攔截住,而因爲 return 而中止函數了
if (this.email === '') {
alert('請輸入 Email,謝謝。');
return;
}

所以依照這個邏輯我們就可以將原本的程式碼優化為以下的版本。

儘早 Return 函數後調整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"
defer
></script>
<style>
* {
box-sizing: border-box;
}

.main {
background: rgb(255, 240, 242);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}

form {
min-width: 400px;
background: rgb(255, 149, 149);
padding: 20px;
border-radius: 10px;
box-shadow: 5px 10px rgba(255, 192, 203, 0.692);
}

input,
label {
display: block;
width: 100%;
margin-bottom: 10px;
text-align: center;
color: #444;
}

input {
padding: 8px;
border-radius: 5px;
border: 1px solid #ddd;
}

button {
border: none;
background: rgb(255, 210, 63);
padding: 10px;
border-radius: 10px;
width: 100%;
font-size: 28px;
color: #666;
color: rgb(53, 53, 53);
}
</style>
</head>
<body>
<div class="main">
<form x-data="formData()">
<label>
UserName
<input type="text" x-model="username" />
</label>
<label>
Email
<input type="email" x-model="email" />
</label>
<label>
Password
<input type="password" x-model="pw" />
</label>
<label>
Address
<input type="text" x-model="address" />
</label>
<label>
Phone
<input type="text" x-model="phone" />
</label>
<button type="submit" @click.prevent="submit">Submit</button>
</form>
</div>
<script>
function formData() {
return {
username: '',
email: '',
pw: '',
address: '',
phone: '',
submit() {
if (this.username === '') {
alert('請輸入帳號,謝謝。');
return;
}

if (this.email === '') {
alert('請輸入 Email,謝謝。');
return;
}

if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email)) {
alert('請輸入 Email 的正確格式,謝謝。');
return;
}

if (this.pw === '') {
alert('請輸入密碼,謝謝。');
return;
}

if (this.address === '') {
alert('請輸入地址,謝謝。');
return;
}

if (this.phone === '') {
alert('請輸入聯絡電話,謝謝。');
return;
}

alert('成功送出囉!');
document.forms[0].submit();
},
};
}
</script>
</body>
</html>

藉由『儘早 Return 函數 』的作法後,我們會發現 If 的階層數都減少到剩下一層,在閱讀的程式碼的時候邏輯也更爲單純,不再把好幾個條件判斷混在一起思考了。

所以若我們想要加上剛剛提過的聯絡電話的驗證,我們就可以加在最底下:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (this.phone === '') {
alert('請輸入聯絡電話,謝謝。');
return;
}

// 加上手機格式的驗證
if (!/^09\d{8}$/.test(this.phone)) {
alert('請輸入手機格式的聯絡電話,謝謝。');
return;
}

alert('成功送出囉!');
document.forms[0].submit();

經過調整過後的程式碼是不是更加簡潔明瞭,以及更好維護了呢?

這個概念其實是來自於之前讀過的《易讀程式之美學》裏面所提到的概念,在寫程式的過程中才發現真的受用無窮,幫助自己將複雜條件判斷簡化,也因爲簡化而大大的減少 bug 的發生機率,真的是一招好棒棒的寫法呀,也分享給大家囉 😍。