เขียนโค้ด JavaScript ด้วยรูปแบบที่ดีกว่า Part I
JavaScript นั้นไม่แตกต่างจากการโปรแกรมในภาษาอื่นที่มีรูปแบบการเขียนโค้ดหลายรูปแต่ให้ผลลัพธ์เดียวกัน บ้างก็เป็นวิธีที่ดี บ้างก็เป็นวิธีฉบับคนเห่ย แต่สำหรับบทความนี้จะแนะนำโค้ด JavaScript ที่ดีกว่า พร้อมเหตุผลแบบชักแม่น้ำทั้งห้าว่าทำไมคุณจึงควรเขียนเช่นนั้น
ใช้ for-of แทน Array.forEach()
กรณีของการวนลูปรอบอาร์เรย์เราสามารถใช้เมธอดของ forEach ของอาร์เรย์ได้ด้วยการส่งฟังก์ชันเป็นอาร์กิวเมนต์ให้กับ forEach ฟังก์ชันดังกล่าวสามารถรับค่าอีลีเมนต์แต่ละตัวในอาร์เรย์และค่า index ได้ด้วย
1const varients = ['alpha', 'beta', 'gramma']23varients.forEach((varient, index) => {4 console.log(`${index}: ${varient}`)5})
ข้อเสียของการใช้เมธอด forEach คือวงเล็กที่มีการซ้อนกันหลายชั้นทำให้อ่านโค้ดยาก นอกจากนี้ภายใต้การทำงานของ forEach เรายังไม่สามารถใช้ break และ continue ได้อีกด้วย
ในที่นี้จึงควรใช้ for-of ซึ่งเป็นรูปแบบการวนรอบที่ทำงานได้ไวกว่า forEach ดังนี้
1const varients = ['alpha', 'beta', 'gramma']23for (const [index, varient] of varients.entries()) {4 console.log(`${index}: ${varient}`)5}
ใช้ Array.indexOf แทนที่ Array.findIndex
ในการค้นหา index ของอีลีเมนต์ภายใต้อาร์เรย์เราสามารถใช้เมธอด findIndex ได้ดังนี้
1const languages = ['Java', 'C#', 'Python', 'Ruby']2languages.findIndex((lang) => lang === 'Python') // 2
findIndex นั้นเหมาะกับการค้นหาที่ซับซ้อน เช่นมีหลายเงื่อนไขเพื่อใช้ประกอบในการพิจารณาเพื่อค้นหาอีลีเมนต์ สำหรับการค้นหาที่เรียบง่ายไม่ซ้ำซ้อนดังเช่นการค้นหา index ของคำที่ต้องการในอาร์เรย์ เราควรใช้ indexOf มากกว่า
1const languages = ['Java', 'C#', 'Python', 'Ruby']2languages.indexOf('Python')
ใช้ Array.at(-1) แทน
กรณีที่เราต้องการเข้าถึงอีลีเมนต์ตัวสุดท้ายของอาร์เรย์เราสามารถทำได้ดังนี้
1const arr = ['A', 'B', 'C']23arr[arr.length - 1]
ECMAScript มีมาตรฐานสำหรับเมธอดใหม่คือ Array.prototype.at ที่ขณะนี้อยู่ในสถานะ Stage 3 ด้วยเมธอดใหม่นี้เราสามารถแปลงโค้ดข้างต้นผ่าน at ได้ดังนี้
1const arr = ['A', 'B', 'C']23arr.at(-1)
กรณีที่ต้องการใช้เมธอด at ที่ยังไม่เป็นมาจรฐานในตอนนี้ สามารถติดตั้งแพคเกจ array.prototype.at เพื่อให้สามารถใช้งานเมธอด at ได้
ระบุเลขติดลบแทนค่า .length - index ในเมธอด slice ของอาร์เรย์
เมธอด slice ของอาร์เรย์นั้นรองรับการใส่เลขลบที่ให้ค่าเหมือนกับการคำนวณความยาวของอาร์เรย์ - 1 เช่น
1// ความยาวอาร์เรย์คือ 42const arr = ['A', 'B', 'C', 'D']34// lenght - 2 = 25// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 2 จนจบอาร์เรย์6arr.slice(-2) // ["C", "D"]78// lenght - 2 = 29// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 1 จนถึงช่องที่ 2 - 110arr.slice(1, -2) // ["B"]1112// lenght - 3 = 113// lenght - 2= 214// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 1 จนถึงช่องที่ 2 - 115arr.slice(-3, -2) // ["B"]
ด้วยเหตุนี้การที่เราต้องการอีลีเมนต์ตั้งแต่ช่องที่ 1 ถึงช่องก่อนสุดท้ายของอาร์เรย์จากเดิมที่เขียนเป็น
1arr.slice(1, arr.length - 1)
จึงสามารถเขียนใหม่ได้ว่า
1arr.slice(1, -1)
ใช้ static properties ของ Number
JavaScript มีฟังก์ชันสำหรับการทำงานกับตัวเลขมากมาย เช่น parseInt, isNan และ isFinite เป็นต้น
1parseInt('20')2parseFloat('33.33')3isNaN(0 / 0)4NaN5Infinity - Infinity
เพื่อให้โค้ดของเราจำแนกความจำเพาะของฟังก์ชันเหล่านี้ได้ว่าเป็นฟังก์ชันของกลุ่มตัวเลข เราจึงควรใช้ฟังก์ชันเหล่านี้ในรูปแบบของ static methods/properties ของ Number มากกว่า
1Number.parseInt('20')2Number.parseFloat('33.33')3Number.isNaN(0 / 0)4Number.NaN5Number.POSITIVE_INFINITY6Number.NEGATIVE_INFINITY
ใช้ TypeError กับการตรวจสอบชนิดข้อมูล
กรณีของการตรวจสอบชนิดข้อมูลหากไม่ตรงตามความต้องการเราอาจโยนข้อผิดพลาดด้วยการสร้าง instance ของ Error ได้ดังนี้
1if (!Array.isArray(arr)) {2 throw new Error('Array expected')3}
เพื่อให้ชนิดของข้อผิดพลาดมีความชัดเจนมากขึ้น เราควรจำเพาะชนิดของข้อผิดพลาดนั้นด้วย TypeError
1if (!Array.isArray(arr)) {2 throw new TypeError('Array expected')3}
ใช้การค้นหาในออบเจ็กต์แทน switch
กรณีที่เรามีตัวแปรค่าหนึ่งแล้วต้องการพิจารณาค่าในตัวแปรดังกล่าวเพื่อสร้างชุดข้อมูลใหม่ เราอาจใช้ if-else หรือ switch-case ในการแก้ปัญหา เช่น
1const country = 'Thailand'2let capitalCity34switch (country) {5 case 'Thailand':6 capitalCity = 'Bangkok'7 break8 case 'Argentina':9 capitalCity = 'Buenos Aires'10 break11 case 'Armenia':12 capitalCity = 'Yerevan'13 break14}
โค้ดข้างต้นอุดมไปด้วยโค้ดซ้ำจากการใส่ break และการกำหนดค่าตัวแปร ทางที่ดีกว่าคือการจัดเก็บ Country และ Capital City ไว้ในออบเจ็กต์ก่อน จากนั้นจึงใช้ตัวแปร capitalCity เพื่อเข้าถึงค่าดังกล่าว
1const country = 'Thailand'2const mapping = {3 Thailand: 'Bangkok',4 Argentina: 'Buenos Aires',5 Armenia: 'Yerevan',6}78const capitalCity = mapping[country]
หรือสามารถลดรูปด้วยการรวบย่อทั้งส่วนประกาศออบเจ็กต์และการค้นหาค่า property ในออบเจ็กต์รวมกันได้ดังนี้
1const country = 'Thailand'2const capitalCity = {3 Thailand: 'Bangkok',4 Argentina: 'Buenos Aires',5 Armenia: 'Yerevan',6}[country]
นอกจากนี้เรายังสามารถใช้ชนิดข้อมูล Map เพื่อแทนโค้ดดังกล่าวได้อีกด้วย
1const country = 'Thailand'2const mapping = new Map([3 ['Thailand', 'Bangkok'],4 ['Argentina', 'Buenos Aires'],5 ['Armenia', 'Yerevan'],6])7const capitalCity = mapping.get(country)
ESLint Plugin Unicorn
eslint-plugin-unicorn เป็นโปรเจครวมรูปแบบการเขียนโค้ดด้วย JavaScript ที่ดีและทันสมัยหลาย ๆ กฎที่นำเสนอในบทความนี้ก็อิงจากโปรเจคนี้ เพื่อให้โค้ด JavaScript ทันสมัยและดีกว่าผู้เขียนจึงแนะนำให้ใช้งาน eslint-plugin-unicorn ควบคู่ไปกับ โปรเจค JavaScript ที่พัฒนาอยู่ครับ
สารบัญ
- ใช้ for-of แทน Array.forEach()
- ใช้ Array.indexOf แทนที่ Array.findIndex
- ใช้ Array.at(-1) แทน
- ระบุเลขติดลบแทนค่า .length - index ในเมธอด slice ของอาร์เรย์
- ใช้ static properties ของ Number
- ใช้ TypeError กับการตรวจสอบชนิดข้อมูล
- ใช้การค้นหาในออบเจ็กต์แทน switch
- ESLint Plugin Unicorn