โค้ดควรสะอาดและอ่านง่ายที่สุดเท่าที่จะทำได้
นี่คือศิลปะส่วนหนึ่งของการเขียนโปรแกรม เราต้องเปลี่ยนโจทย์ที่ซับซ้อนให้เป็นโค้ดที่ทั้งทำงานถูกต้องและคนอ่านเข้าใจได้ สไตล์การเขียนโค้ดที่ดีช่วยเรื่องนี้ได้มาก
ภาพนี้สรุปกฎที่แนะนำไว้ ส่วนเหตุผลของแต่ละข้อจะอธิบายต่อจากนี้:
ทีนี้มาดูกฎแต่ละข้อและเหตุผลเบื้องหลังกัน
กฎเหล่านี้ไม่ได้ตายตัว เป็นเพียงความชอบเรื่องรูปแบบ ไม่ใช่กฎเหล็กที่ทุกคนต้องทำตาม
โปรเจกต์ JavaScript ส่วนใหญ่เขียนวงเล็บปีกกาแบบ "Egyptian" คือวางวงเล็บเปิดไว้บรรทัดเดียวกับคีย์เวิร์ดที่เกี่ยวข้อง ไม่แยกไปขึ้นบรรทัดใหม่ และเว้นวรรคก่อนวงเล็บเปิดแบบนี้:
if (condition) {
// ทำสิ่งนี้
// ...แล้วทำสิ่งนั้น
// ...แล้วก็สิ่งนั้นด้วย
}จุดที่ต้องคิดเพิ่มคือคำสั่งสั้น ๆ บรรทัดเดียวอย่าง if (condition) doSomething() เราควรใส่วงเล็บปีกกาหรือไม่?
ลองเทียบตัวเลือกพร้อมคำอธิบาย แล้วดูว่าแบบไหนอ่านง่ายที่สุด:
- 😠 มือใหม่บางคนเขียนแบบนี้ แต่ไม่ดี เพราะกรณีนี้ไม่จำเป็นต้องใส่วงเล็บปีกกา:
if (n < 0) *!*{*/!*alert(`Power ${n} is not supported`);*!*}*/!*
- 😠 แยกคำสั่งไปอีกบรรทัดแต่ไม่ใส่วงเล็บปีกกา อย่าเขียนแบบนี้ เพราะพลาดได้ง่ายเมื่อเพิ่มคำสั่งใหม่:
if (n < 0) alert(`Power ${n} is not supported`);
- 😏 เขียนบรรทัดเดียวโดยไม่ใส่วงเล็บปีกกา ถ้าโค้ดสั้นก็พอรับได้:
if (n < 0) alert(`Power ${n} is not supported`);
- 😃 แบบที่ดีที่สุด:
if (n < 0) { alert(`Power ${n} is not supported`); }
ถ้าโค้ดสั้นมาก จะเขียนบรรทัดเดียวอย่าง if (cond) return null ก็ได้ แต่โดยทั่วไปการเขียนเป็นบล็อกแบบสุดท้ายอ่านง่ายกว่า
โค้ดที่ยาวต่อกันในแนวนอนอ่านยาก จึงควรแบ่งออกเป็นหลายบรรทัด
ตัวอย่างเช่น:
// เครื่องหมาย backtick ` ช่วยแบ่งสตริงเป็นหลายบรรทัดได้
let str = `
ECMA International's TC39 is a group of JavaScript developers,
implementers, academics, and more, collaborating with the community
to maintain and evolve the definition of JavaScript.
`;คำสั่ง if ก็แบ่งได้เช่นกัน:
if (
id === 123 &&
moonPhase === 'Waning Gibbous' &&
zodiacSign === 'Libra'
) {
letTheSorceryBegin();
}ทีมควรตกลงกันว่าหนึ่งบรรทัดยาวได้ไม่เกินเท่าไร ค่าที่มักใช้กันคือ 80 หรือ 120 ตัวอักษร
การเยื้องบรรทัดมี 2 แบบ:
-
การเยื้องแนวนอน: ใช้ช่องว่าง 2 หรือ 4 ช่อง
เราเยื้องแนวนอนด้วยช่องว่าง 2 หรือ 4 ช่อง หรือใช้แท็บ (ปุ่ม
key:Tab) ก็ได้ เรื่องว่าจะเลือกแบบไหนถกเถียงกันมานาน หลายโปรเจกต์นิยมใช้ช่องว่างมากกว่าข้อดีอย่างหนึ่งของช่องว่างคือกำหนดระยะเยื้องได้ยืดหยุ่นกว่าแท็บ
เช่น เราจัดพารามิเตอร์ให้ตรงกับวงเล็บเปิดได้แบบนี้:
show(parameters, aligned, // เว้นจากด้านซ้าย 5 ช่อง one, after, another ) { // ... }
-
การเว้นระยะในแนวตั้ง: ใช้บรรทัดว่างแบ่งโค้ดเป็นกลุ่มตามหน้าที่
แม้มีแค่ฟังก์ชันเดียว เราก็มักแบ่งโค้ดเป็นกลุ่มย่อยได้ ตัวอย่างนี้ใช้บรรทัดว่างแยกการกำหนดค่าเริ่มต้น ลูปหลัก และการคืนค่าออกจากกัน:
function pow(x, n) { let result = 1; // <-- for (let i = 0; i < n; i++) { result *= x; } // <-- return result; }
เพิ่มบรรทัดว่างตรงจุดที่ช่วยให้อ่านง่ายขึ้น ไม่ควรปล่อยให้โค้ดติดกันเกิน 9 บรรทัดโดยไม่มีบรรทัดว่างคั่นเลย
ควรใส่เครื่องหมายเซมิโคลอนหลังทุกคำสั่ง แม้บางตำแหน่งจะละได้ก็ตาม
บางภาษาไม่บังคับใช้เซมิโคลอนและแทบไม่มีใครใส่ แต่ JavaScript ไม่ได้มองการขึ้นบรรทัดใหม่เป็นเซมิโคลอนเสมอ จึงอาจทำให้เกิดข้อผิดพลาดได้ อ่านรายละเอียดเพิ่มในบท info:structure#semicolon
ถ้าคุ้นเคยกับ JavaScript ดีแล้ว จะเลือกสไตล์ที่ไม่ใส่เซมิโคลอนอย่าง StandardJS ก็ได้ แต่ถ้ายังไม่แน่ใจ ใส่ไว้จะช่วยเลี่ยงปัญหาที่อาจเกิดขึ้น นักพัฒนาส่วนใหญ่ใช้เซมิโคลอนกัน
พยายามอย่าซ้อนโค้ดลึกเกินไป
ตัวอย่างเช่น ในลูป บางครั้งการใช้คำสั่ง continue ช่วยลดการซ้อนโค้ดได้
แทนที่จะเพิ่มเงื่อนไข if ซ้อนเข้าไปแบบนี้:
for (let i = 0; i < 10; i++) {
if (cond) {
... // <- ซ้อนเพิ่มอีกหนึ่งระดับ
}
}เราเขียนแบบนี้ได้:
for (let i = 0; i < 10; i++) {
if (!cond) *!*continue*/!*;
... // <- ไม่ต้องซ้อนเพิ่ม
}ใช้วิธีคล้ายกันนี้กับ if/else และ return ได้
ตัวอย่างต่อไปนี้ทั้งสองแบบให้ผลเหมือนกัน
แบบที่ 1:
function pow(x, n) {
if (n < 0) {
alert("Negative 'n' not supported");
} else {
let result = 1;
for (let i = 0; i < n; i++) {
result *= x;
}
return result;
}
}แบบที่ 2:
function pow(x, n) {
if (n < 0) {
alert("Negative 'n' not supported");
return;
}
let result = 1;
for (let i = 0; i < n; i++) {
result *= x;
}
return result;
}แบบที่ 2 อ่านง่ายกว่า เพราะจัดการ "กรณีพิเศษ" ที่ n < 0 ตั้งแต่ต้น พอตรวจเงื่อนไขนี้เสร็จ ก็อ่านลำดับการทำงานหลักต่อได้เลยโดยไม่ต้องตามโค้ดที่ซ้อนลึกลงไปอีก
ถ้ามีฟังก์ชัน "ตัวช่วย" หลายตัวและมีโค้ดที่เรียกใช้ เราจัดวางได้ 3 แบบ
-
ประกาศฟังก์ชันไว้ เหนือ โค้ดที่เรียกใช้:
// *!*การประกาศฟังก์ชัน*/!* function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... } // *!*โค้ดที่เรียกใช้ฟังก์ชัน*/!* let elem = createElement(); setHandler(elem); walkAround();
-
วางโค้ดหลักก่อน แล้วค่อยตามด้วยฟังก์ชัน
// *!*โค้ดที่เรียกใช้ฟังก์ชัน*/!* let elem = createElement(); setHandler(elem); walkAround(); // --- *!*ฟังก์ชันตัวช่วย*/!* --- function createElement() { ... } function setHandler(elem) { ... } function walkAround() { ... }
-
ผสมสองแบบเข้าด้วยกัน โดยประกาศฟังก์ชันตรงจุดที่เรียกใช้ครั้งแรก
ส่วนใหญ่นิยมใช้แบบที่ 2
เหตุผลคือเวลาอ่านโค้ด เรามักอยากรู้ก่อนว่า โค้ดทำอะไร ถ้าวางโค้ดหลักไว้ก่อน ภาพรวมจะชัดตั้งแต่ต้น และอาจไม่ต้องไล่อ่านฟังก์ชันด้านล่างเลย โดยเฉพาะเมื่อชื่อฟังก์ชันบอกหน้าที่ชัดอยู่แล้ว
คู่มือสไตล์รวบรวมกฎทั่วไปเรื่องวิธีเขียนโค้ด เช่น ใช้เครื่องหมายคำพูดแบบไหน เยื้องกี่ช่อง หรือกำหนดความยาวบรรทัดสูงสุดเท่าไร ทั้งหมดเป็นรายละเอียดเล็ก ๆ ที่มีอยู่มากมาย
เมื่อทุกคนในทีมใช้คู่มือสไตล์เดียวกัน โค้ดจะมีรูปแบบสม่ำเสมอไม่ว่าใครเป็นคนเขียน
ทีมจะเขียนคู่มือขึ้นมาเองก็ได้ แต่โดยทั่วไปไม่จำเป็น เพราะมีคู่มือให้เลือกใช้อยู่แล้วหลายชุด
ตัวอย่างคู่มือที่ได้รับความนิยม:
- Google JavaScript Style Guide
- Airbnb JavaScript Style Guide
- Idiomatic.JS
- StandardJS
- (และยังมีอีกมาก)
ถ้าเพิ่งเริ่มเขียนโปรแกรม ให้เริ่มจากภาพสรุปต้นบทก่อน แล้วค่อยเปิดดูคู่มือชุดอื่นเพื่อเก็บแนวคิดเพิ่มและเลือกสไตล์ที่ชอบ
Linter คือเครื่องมือที่ตรวจสไตล์ของโค้ดให้อัตโนมัติ พร้อมแนะนำจุดที่ควรแก้
ข้อดีคือระหว่างตรวจสไตล์ linter อาจเจอบั๊กบางอย่างด้วย เช่น พิมพ์ชื่อตัวแปรหรือฟังก์ชันผิด จึงควรใช้ linter แม้ไม่ได้ตั้งใจยึด "สไตล์การเขียนโค้ด" ชุดใดเป็นพิเศษ
ตัวอย่าง linter ที่รู้จักกันแพร่หลาย:
- JSLint — เป็นหนึ่งใน linter รุ่นแรก ๆ
- JSHint — ตั้งค่าได้มากกว่า JSLint
- ESLint — น่าจะใหม่ที่สุดในกลุ่มนี้
ทุกตัวตรวจโค้ดได้ดี ส่วนผู้เขียนใช้ ESLint
Linter ส่วนใหญ่เชื่อมต่อกับ editor ยอดนิยมได้ เพียงเปิดใช้ปลั๊กอินใน editor แล้วกำหนดสไตล์ที่ต้องการ
ถ้าจะใช้ ESLint ให้ทำตามขั้นตอนนี้:
- ติดตั้ง Node.js
- ติดตั้ง ESLint ด้วยคำสั่ง
npm install -g eslint(npm คือตัวติดตั้งแพ็กเกจของ JavaScript) - สร้างไฟล์ตั้งค่าชื่อ
.eslintrcไว้ที่โฟลเดอร์หลักของโปรเจกต์ JavaScript (โฟลเดอร์ที่เก็บไฟล์ทั้งหมด) - ติดตั้งหรือเปิดใช้ปลั๊กอิน ESLint สำหรับ editor ที่ใช้ โดย editor ส่วนใหญ่มีปลั๊กอินนี้
ตัวอย่างไฟล์ .eslintrc:
{
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"no-console": 0,
"indent": 2
}
}ค่า "extends" ตรงนี้หมายถึงให้นำชุดตั้งค่า "eslint:recommended" มาเป็นพื้นฐาน แล้วจึงเพิ่มค่าของเราเอง
เรายังดาวน์โหลดชุดกฎสไตล์จากเว็บมาต่อยอดได้ ดูรายละเอียดการติดตั้งเพิ่มเติมที่ https://eslint.org/docs/user-guide/getting-started
IDE บางตัวมี linter ในตัว ใช้งานสะดวก แต่ปรับแต่งได้น้อยกว่า ESLint
กฎเรื่องรูปแบบโค้ดทั้งหมดในบทนี้ รวมถึงกฎจากคู่มือสไตล์ที่อ้างถึง มีเป้าหมายเดียวกันคือช่วยให้โค้ดอ่านง่ายขึ้น และทุกข้อก็ถกเถียงกันได้
เมื่อคิดว่าจะเขียนโค้ดให้ "ดีขึ้น" อย่างไร ให้ถามว่า "อะไรทำให้โค้ดอ่านและเข้าใจง่ายขึ้น?" และ "อะไรช่วยให้เราเลี่ยงข้อผิดพลาดได้?" สองคำถามนี้คือสิ่งสำคัญในการเลือกและพูดคุยเรื่องสไตล์การเขียนโค้ด
อ่านคู่มือสไตล์ยอดนิยมเป็นระยะ ๆ จะช่วยให้ตามแนวคิดเรื่องสไตล์โค้ดและแนวปฏิบัติที่ดีได้ทัน