เขียนโค้ด JavaScript ด้วยรูปแบบที่ดีกว่า Part I

Nuttavut Thongjor

JavaScript นั้นไม่แตกต่างจากการโปรแกรมในภาษาอื่นที่มีรูปแบบการเขียนโค้ดหลายรูปแต่ให้ผลลัพธ์เดียวกัน บ้างก็เป็นวิธีที่ดี บ้างก็เป็นวิธีฉบับคนเห่ย แต่สำหรับบทความนี้จะแนะนำโค้ด JavaScript ที่ดีกว่า พร้อมเหตุผลแบบชักแม่น้ำทั้งห้าว่าทำไมคุณจึงควรเขียนเช่นนั้น

ใช้ for-of แทน Array.forEach()

กรณีของการวนลูปรอบอาร์เรย์เราสามารถใช้เมธอดของ forEach ของอาร์เรย์ได้ด้วยการส่งฟังก์ชันเป็นอาร์กิวเมนต์ให้กับ forEach ฟังก์ชันดังกล่าวสามารถรับค่าอีลีเมนต์แต่ละตัวในอาร์เรย์และค่า index ได้ด้วย

Code
1const varients = ['alpha', 'beta', 'gramma']
2
3varients.forEach((varient, index) => {
4 console.log(`${index}: ${varient}`)
5})

ข้อเสียของการใช้เมธอด forEach คือวงเล็กที่มีการซ้อนกันหลายชั้นทำให้อ่านโค้ดยาก นอกจากนี้ภายใต้การทำงานของ forEach เรายังไม่สามารถใช้ break และ continue ได้อีกด้วย

ในที่นี้จึงควรใช้ for-of ซึ่งเป็นรูปแบบการวนรอบที่ทำงานได้ไวกว่า forEach ดังนี้

Code
1const varients = ['alpha', 'beta', 'gramma']
2
3for (const [index, varient] of varients.entries()) {
4 console.log(`${index}: ${varient}`)
5}

ใช้ Array.indexOf แทนที่ Array.findIndex

ในการค้นหา index ของอีลีเมนต์ภายใต้อาร์เรย์เราสามารถใช้เมธอด findIndex ได้ดังนี้

Code
1const languages = ['Java', 'C#', 'Python', 'Ruby']
2languages.findIndex((lang) => lang === 'Python') // 2

findIndex นั้นเหมาะกับการค้นหาที่ซับซ้อน เช่นมีหลายเงื่อนไขเพื่อใช้ประกอบในการพิจารณาเพื่อค้นหาอีลีเมนต์ สำหรับการค้นหาที่เรียบง่ายไม่ซ้ำซ้อนดังเช่นการค้นหา index ของคำที่ต้องการในอาร์เรย์ เราควรใช้ indexOf มากกว่า

Code
1const languages = ['Java', 'C#', 'Python', 'Ruby']
2languages.indexOf('Python')

ใช้ Array.at(-1) แทน

กรณีที่เราต้องการเข้าถึงอีลีเมนต์ตัวสุดท้ายของอาร์เรย์เราสามารถทำได้ดังนี้

Code
1const arr = ['A', 'B', 'C']
2
3arr[arr.length - 1]

ECMAScript มีมาตรฐานสำหรับเมธอดใหม่คือ Array.prototype.at ที่ขณะนี้อยู่ในสถานะ Stage 3 ด้วยเมธอดใหม่นี้เราสามารถแปลงโค้ดข้างต้นผ่าน at ได้ดังนี้

Code
1const arr = ['A', 'B', 'C']
2
3arr.at(-1)

กรณีที่ต้องการใช้เมธอด at ที่ยังไม่เป็นมาจรฐานในตอนนี้ สามารถติดตั้งแพคเกจ array.prototype.at เพื่อให้สามารถใช้งานเมธอด at ได้

ระบุเลขติดลบแทนค่า .length - index ในเมธอด slice ของอาร์เรย์

เมธอด slice ของอาร์เรย์นั้นรองรับการใส่เลขลบที่ให้ค่าเหมือนกับการคำนวณความยาวของอาร์เรย์ - 1 เช่น

Code
1// ความยาวอาร์เรย์คือ 4
2const arr = ['A', 'B', 'C', 'D']
3
4// lenght - 2 = 2
5// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 2 จนจบอาร์เรย์
6arr.slice(-2) // ["C", "D"]
7
8// lenght - 2 = 2
9// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 1 จนถึงช่องที่ 2 - 1
10arr.slice(1, -2) // ["B"]
11
12// lenght - 3 = 1
13// lenght - 2= 2
14// slice จึงหมายถึงดึงอีลีเมนต์ตั้งแต่ช่องที่ 1 จนถึงช่องที่ 2 - 1
15arr.slice(-3, -2) // ["B"]

ด้วยเหตุนี้การที่เราต้องการอีลีเมนต์ตั้งแต่ช่องที่ 1 ถึงช่องก่อนสุดท้ายของอาร์เรย์จากเดิมที่เขียนเป็น

Code
1arr.slice(1, arr.length - 1)

จึงสามารถเขียนใหม่ได้ว่า

Code
1arr.slice(1, -1)

ใช้ static properties ของ Number

JavaScript มีฟังก์ชันสำหรับการทำงานกับตัวเลขมากมาย เช่น parseInt, isNan และ isFinite เป็นต้น

Code
1parseInt('20')
2parseFloat('33.33')
3isNaN(0 / 0)
4NaN
5Infinity - Infinity

เพื่อให้โค้ดของเราจำแนกความจำเพาะของฟังก์ชันเหล่านี้ได้ว่าเป็นฟังก์ชันของกลุ่มตัวเลข เราจึงควรใช้ฟังก์ชันเหล่านี้ในรูปแบบของ static methods/properties ของ Number มากกว่า

Code
1Number.parseInt('20')
2Number.parseFloat('33.33')
3Number.isNaN(0 / 0)
4Number.NaN
5Number.POSITIVE_INFINITY
6Number.NEGATIVE_INFINITY

ใช้ TypeError กับการตรวจสอบชนิดข้อมูล

กรณีของการตรวจสอบชนิดข้อมูลหากไม่ตรงตามความต้องการเราอาจโยนข้อผิดพลาดด้วยการสร้าง instance ของ Error ได้ดังนี้

Code
1if (!Array.isArray(arr)) {
2 throw new Error('Array expected')
3}

เพื่อให้ชนิดของข้อผิดพลาดมีความชัดเจนมากขึ้น เราควรจำเพาะชนิดของข้อผิดพลาดนั้นด้วย TypeError

Code
1if (!Array.isArray(arr)) {
2 throw new TypeError('Array expected')
3}

ใช้การค้นหาในออบเจ็กต์แทน switch

กรณีที่เรามีตัวแปรค่าหนึ่งแล้วต้องการพิจารณาค่าในตัวแปรดังกล่าวเพื่อสร้างชุดข้อมูลใหม่ เราอาจใช้ if-else หรือ switch-case ในการแก้ปัญหา เช่น

Code
1const country = 'Thailand'
2let capitalCity
3
4switch (country) {
5 case 'Thailand':
6 capitalCity = 'Bangkok'
7 break
8 case 'Argentina':
9 capitalCity = 'Buenos Aires'
10 break
11 case 'Armenia':
12 capitalCity = 'Yerevan'
13 break
14}

โค้ดข้างต้นอุดมไปด้วยโค้ดซ้ำจากการใส่ break และการกำหนดค่าตัวแปร ทางที่ดีกว่าคือการจัดเก็บ Country และ Capital City ไว้ในออบเจ็กต์ก่อน จากนั้นจึงใช้ตัวแปร capitalCity เพื่อเข้าถึงค่าดังกล่าว

Code
1const country = 'Thailand'
2const mapping = {
3 Thailand: 'Bangkok',
4 Argentina: 'Buenos Aires',
5 Armenia: 'Yerevan',
6}
7
8const capitalCity = mapping[country]

หรือสามารถลดรูปด้วยการรวบย่อทั้งส่วนประกาศออบเจ็กต์และการค้นหาค่า property ในออบเจ็กต์รวมกันได้ดังนี้

Code
1const country = 'Thailand'
2const capitalCity = {
3 Thailand: 'Bangkok',
4 Argentina: 'Buenos Aires',
5 Armenia: 'Yerevan',
6}[country]

นอกจากนี้เรายังสามารถใช้ชนิดข้อมูล Map เพื่อแทนโค้ดดังกล่าวได้อีกด้วย

Code
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