Developer Team
BorntoDev Co., Ltd.
ตั้งชื่อให้สื่อความหมาย
ส่วนที่ง่ายที่สุดในการทำให้โค้ดของเราดูเป็นมือโปรก็คือการตั้งชื่อ ไม่ว่าจะเป็นตัวแปรหรือชื่อฟังก์ชัน เราควรหลีกเลี่ยงการตั้งชื่อด้วยตัวแปรที่ไม่สื่อความหมายหรือทำให้เกิดความเข้าใจผิด ตัวอย่างเช่น
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 ของเราก็จะออกมาดูเหมือนมือโปรขึ้นเยอะ เพราะว่าสิ่งสำคัญในการเขียนโค้ดไม่ใช่การเขียนให้ทำงานได้ แต่เป็นการทำให้โค้ดแก้ไขง่ายและอ่านเข้าใจได้นั่นเองครับ
บทความนี้อ้างอิงเนื้อหาและโค้ดตัวอย่างจากวีดีโอด้านล่างนี้นะครับ ถ้าใครสนใจจะดูแบบเต็มๆอธิบายทีละบรรทัดละก็คลิกเข้าไปดูกันได้ เค้าอธิบายเอาไว้ได้ดีมากๆ