ภาษาโปรแกรมที่มีประโยชน์ในการนำไปใช้งานได้หลากหลาย ได้ทั้ง Frontend และ Backend อย่าง JavaScript เชื่อว่าทุกคนที่รู้จักการเขียนโปรแกรมก็ต้องรู้จักหรืออาจจะเคยเขียนกันมา วันนี้เราจะมาดูกันว่าจะเขียน JavaScript ยังไงให้ดูเป็นมือโปร
ตั้งชื่อให้สื่อความหมาย
ส่วนที่ง่ายที่สุดในการทำให้โค้ดของเราดูเป็นมือโปรก็คือการตั้งชื่อ ไม่ว่าจะเป็นตัวแปรหรือชื่อฟังก์ชัน เราควรหลีกเลี่ยงการตั้งชื่อด้วยตัวแปรที่ไม่สื่อความหมายหรือทำให้เกิดความเข้าใจผิด ตัวอย่างเช่น
function toAccounting(n) {
if (n < 0) {
return '(' + Math.abs(n) + ')'
} else if (n >= 0) {
return n
}
}
สมมติให้มีฟังก์ชันคำนวนตัวเลขทางบัญชี แบบโค้ดด้านบนนี้ จะเห็นว่าการตั้งชื่อฟังก์ชันยังไม่สื่อความหมายชัดเจน “toAccounting” ถ้าเราไม่ได่เป็นคนเขียนฟังก์ชันนี้ขึ้นมาหรือว่าโค้ดทั้งหมดมีหลายร้อยบรรทัด เวลาที่เราย้อนกลับมาดูก็อาจจะต้องเสียเวลาทำความเข้าใจว่าฟังก์ชันตัวนี้มีไว้เพื่ออะไร แล้วใช้งานยังไง และพารามิเตอร์ที่รับเข้าไปก็ชื่อว่า “n” ยิ่งทำให้ชวนสับสวนเข้าไปใหญ่ว่าต้องใส่ค่าอะไรเข้าไป เราสามารถเปลี่ยนฟังก์ชันนี้ให้มีชื่อที่ดีขึ้นได้ เช่น
- “toAccounting” >> “numberToAccounting”
- “n” >> “number”
โค้ดใหม่ของเราจะได้เป็นแบบนี้
function numberToAccounting(number) {
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else if (number >= 0) {
return number
}
}
ฟังก์ชันควรรีเทิร์นค่าเพียงชนิดเดียว
จากฟังก์ชัน numberToAccounting จะเหนว่าใน if กับ else มีการ return ค่าออกมาต่างชนิดกัน โดยถ้าหากเข้าเงื่อนไข if จะได้ผลลัพธ์เป็น ข้อความ(string)แต่ถ้าเข้าใน else จะได้ผลลัพธ์เป็น ตัวเลข(number) เช่น
function numberToAccounting(number) {
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else if (number >= 0) {
return number
}
}
console.log(numberToAccounting(-5))
console.log(typeof numberToAccounting(-5))
console.log(numberToAccounting(10))
console.log(typeof numberToAccounting(10))
ผลลัพธ์
(5)
string
10
number
เราต้องเลือกว่าจะให้ฟังก์ชันของเรา return เป็นข้อความหรือตัวเลข สมมติเราเลือกข้อความเราก็จัดการแก้โค้ดในส่วนของ else ให้ return เป็นข้อความซะ แล้วก็อาจจะเปลี่ยนชื่อฟังก์ชันเพื่อให้อ่านแล้วเข้าใจได้การทำงานได้ดีขึ้นอีกจาก “numberToAccounting” เป็น “numberToAccountingString”
function numberToAccountingString(number) {
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else if (number >= 0) {
return number.toString()
}
}
console.log(numberToAccountingString(-5))
console.log(typeof numberToAccountingString(-5))
console.log(numberToAccountingString(10))
console.log(typeof numberToAccountingString(10))
คราวนี้ผลลัพธ์ก็จะเป็น String ไม่ว่าจะเข้าเงื่อนไขไหนก็ตาม
(5)
string
10
string
ลดการทำงานที่ไม่จำเป็น
หลายครั้งฟังก์ชันที่ทำงานซับซ้อนมีการเช็คเงื่อนไขมากมาย ก็ไม่สามารถทำงานได้เพียงเพราะพารามิเตอร์ที่ส่งเข้ามาไม่ถูกต้อง ซึ่งถ้าเรารู้อยู่แล้วว่ามีกรณีแบบไหนที่ฟังก์ชันของเราจะทำงานไม่ได้หรือไม่ต้องการให้ทำงาน เราก็ควรจัดเขียนเงื่อนไขเช็คตั้งแต่เริ่มเข้าฟังก์ชันซะก่อน อย่างในฟังก์ชัน numberToAccountingString ถ้าค่าที่รับเข้ามาไม่ใช่ตัวเลขก็จะเกิด error ได้แบบนี้
function numberToAccountingString(number) {
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else if (number >= 0) {
return number.toString()
}
}
console.log(numberToAccountingString(null))
ผลลัพธ์
error: Uncaught TypeError: Cannot read property 'toString' of null
จะเห็นว่ากว่าจะเกิด error ขึ้นในบรรทัดที่แปลง number ไปเป็น string ฟังก์ชันของเราก็ทำงานไปหลายบรรทัดแล้ว ซึ่งในกรณีแบบนี้เราสามารถให้หยุดการทำงานตั้งแต่เข้ามาในฟังก์ชันได้เลย ด้วยการสร้าง Guard Clause ขึ้นมาเช็คแบบโค้ดด้านล่างนี้
function numberToAccountingString(number) {
if (number == null || !number.isNumeric()) return
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else if (number >= 0) {
return number.toString()
}
}
console.log(numberToAccountingString(null))
ในโค้ดนี้ยังมีสิ่งที่หลายๆคนมองข้ามไปในตอนที่เขียนโค้ดก็คือการเช็คเงื่อนไข จะเห็นว่าฟังก์ชันของเรามีการทำงาน 2 แบบก็คือกรณีที่ number น้อยกว่า 0 และกรณีที่ number มีค่าเป็น 0 ขึ้นไป ซึ่งในโค้ดเราเช็คใน if ว่า
if (number < 0)
โค้ดตรงนี้ก็ครอบคลุมในกรณีแรกครบแล้ว กรณีที่ไม่เข้าเงื่อนไขนี้ก็หมายความว่าจะเป็นกรณีที่ number เป็น 0 มากกว่า 0 แน่นอน แต่ในโค้ดของเราก็ยังเขียนเพื่อเช็คอีกรอบอยู่
else if (number >= 0)
ซึ่งเป็นโค้ดที่ไม่จำเป็น เราสามารถตัดออกไปได้โดยที่ฟังก์ชันยังทำงานได้เหมือนเดิม แถมยังลดความ’เยอะ’หรือ’ซับซ้อน’ของโค้ดโดยรวมลงได้
function numberToAccountingString(number){
if (number == null || !number.isNumeric()) return
if (number < 0) {
return '(' + Math.abs(number) + ')'
} else {
return number.toString()
}
}
ไม่ใช้ตัวแปรเดียวสำหรับทำทุกอย่าง
ในการเขียนโค้ดที่ต้องทำงานหลายขั้นตอนเพื่อให้ได้ผลลัพธ์ออกมา การสร้างตัวแปรเพื่อใช้ภายในฟังก์ชันก็เป็นสิ่งสำคัญที่อาจถูกมองข้าม ลองดูจากโค้ดคำนวนราคาด้านล่างนี้
const TAX_RATE = 1.1
const SHIPPING_DEFAULT = 5
function calculateTotal(items, options = {}) {
if (items == null || items.lenght === 0) return 0
let total = 0
items.forEach(item => {
total += item.price * item.quantity
})
total = total - total * (options.discount || 0)
total = total * TAX_RATE
if (options.shipping !== 0) {
total = total + (options.shipping || SHIPPING_DEFAULT)
}
return total
}
const testItem = [
{ price: 15, quantity: 2 },
{ price: 20, quantity: 1 },
{ price: 5, quantity: 4 }
]
console.log(calculateTotal(testItem, {}))
console.log(calculateTotal(testItem, { shipping: 0 }))
console.log(calculateTotal(testItem, { discount: .75 }))
console.log(calculateTotal(testItem, { shipping: 12 }))
ผลลัพธ์
82
77
24.25
89
ตัวแปร total ในฟังก์ชันนั้นถูกสร้างมาตั้งแต่เข้าฟังก์ชันและใช้การอัพเดตค่าในตัวแปรเพื่อเก็บราคารวม ราคาที่คำนวนส่วนลด และราคาหลังคิดภาษี ซึ่งได้ผลลัพธ์การทำงานถูกต้อง แต่ว่าถ้าเราย้อนกลับมาแก้ไขฟังก์ชันนี้ในอนาคต หรือถ้าหากว่าฟังก์ชันนี้มีการทำงานที่ยาวเป็นร้อยบรรทัด การที่เราจะแก้ไขบางอย่างกับตัวแปร total ก็อาจจะกระทบทั้งฟังก์ชันเลยก็ได้ เราจึงควรสร้างตัวแปรเพื่อใช้เก็บข้อมูลหรือผลลัพธ์ให้ตรงกับการทำงาน อย่างโค้ดคำนวนราคาก็จะได้ออกมาเป็น
const TAX_RATE = 1.1
const SHIPPING_DEFAULT = 5
function calculateTotal(items, options = {}) {
if (items == null || items.lenght === 0) return 0
let itemCost = 0
items.forEach(item => {
itemCost += item.price * item.quantity
})
let discountRate = 1 - (options.discount || 0)
let shipping = SHIPPING_DEFAULT
if( options.shipping || options.shipping === 0) {
shipping = options.shipping
}
return itemCost * discountRate * TAX_RATE + shipping
}
const testItem = [
{ price: 15, quantity: 2 },
{ price: 20, quantity: 1 },
{ price: 5, quantity: 4 }
]
console.log(calculateTotal(testItem, {}))
console.log(calculateTotal(testItem, { shipping: 0 }))
console.log(calculateTotal(testItem, { discount: .75 }))
console.log(calculateTotal(testItem, { shipping: 12 }))
ผลลัพธ์
82
77
24.25
89
จากวิธีต่างๆที่ยกตัวอย่างมาในครั้งนี้ก็เป็นส่วนเล็กๆที่เราสามารถนำไปปรับใช้กับการเขียน JavaScript ของเราได้ง่ายๆ บางข้ออาจจะเป็นเรื่องพื้นฐานอย่างการตั้งชื่อตัวแปร หรือบางข้อก็เป็นเรื่องเล็กๆน้อยๆอย่างชนิดข้อมูลที่รีเทิร์น แต่ว่าทั้งหมดนี้ถ้าเรานำไปใช้โค้ด JavaScript ของเราก็จะออกมาดูเหมือนมือโปรขึ้นเยอะ เพราะว่าสิ่งสำคัญในการเขียนโค้ดไม่ใช่การเขียนให้ทำงานได้ แต่เป็นการทำให้โค้ดแก้ไขง่ายและอ่านเข้าใจได้นั่นเองครับ
บทความนี้อ้างอิงเนื้อหาและโค้ดตัวอย่างจากวีดีโอด้านล่างนี้นะครับ ถ้าใครสนใจจะดูแบบเต็มๆอธิบายทีละบรรทัดละก็คลิกเข้าไปดูกันได้ เค้าอธิบายเอาไว้ได้ดีมากๆ