Skip to content

Latest commit

 

History

History
348 lines (251 loc) · 18.6 KB

File metadata and controls

348 lines (251 loc) · 18.6 KB

สไตล์การเขียนโค้ด

โค้ดควรสะอาดและอ่านง่ายที่สุดเท่าที่จะทำได้

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

รูปแบบโค้ด

ภาพนี้สรุปกฎที่แนะนำไว้ ส่วนเหตุผลของแต่ละข้อจะอธิบายต่อจากนี้:

ทีนี้มาดูกฎแต่ละข้อและเหตุผลเบื้องหลังกัน

กฎเหล่านี้ไม่ได้ตายตัว เป็นเพียงความชอบเรื่องรูปแบบ ไม่ใช่กฎเหล็กที่ทุกคนต้องทำตาม

วงเล็บปีกกา

โปรเจกต์ JavaScript ส่วนใหญ่เขียนวงเล็บปีกกาแบบ "Egyptian" คือวางวงเล็บเปิดไว้บรรทัดเดียวกับคีย์เวิร์ดที่เกี่ยวข้อง ไม่แยกไปขึ้นบรรทัดใหม่ และเว้นวรรคก่อนวงเล็บเปิดแบบนี้:

if (condition) {
  // ทำสิ่งนี้
  // ...แล้วทำสิ่งนั้น
  // ...แล้วก็สิ่งนั้นด้วย
}

จุดที่ต้องคิดเพิ่มคือคำสั่งสั้น ๆ บรรทัดเดียวอย่าง if (condition) doSomething() เราควรใส่วงเล็บปีกกาหรือไม่?

ลองเทียบตัวเลือกพร้อมคำอธิบาย แล้วดูว่าแบบไหนอ่านง่ายที่สุด:

  1. 😠 มือใหม่บางคนเขียนแบบนี้ แต่ไม่ดี เพราะกรณีนี้ไม่จำเป็นต้องใส่วงเล็บปีกกา:
    if (n < 0) *!*{*/!*alert(`Power ${n} is not supported`);*!*}*/!*
  2. 😠 แยกคำสั่งไปอีกบรรทัดแต่ไม่ใส่วงเล็บปีกกา อย่าเขียนแบบนี้ เพราะพลาดได้ง่ายเมื่อเพิ่มคำสั่งใหม่:
    if (n < 0)
      alert(`Power ${n} is not supported`);
  3. 😏 เขียนบรรทัดเดียวโดยไม่ใส่วงเล็บปีกกา ถ้าโค้ดสั้นก็พอรับได้:
    if (n < 0) alert(`Power ${n} is not supported`);
  4. 😃 แบบที่ดีที่สุด:
    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 แบบ

  1. ประกาศฟังก์ชันไว้ เหนือ โค้ดที่เรียกใช้:

    // *!*การประกาศฟังก์ชัน*/!*
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
    
    // *!*โค้ดที่เรียกใช้ฟังก์ชัน*/!*
    let elem = createElement();
    setHandler(elem);
    walkAround();
  2. วางโค้ดหลักก่อน แล้วค่อยตามด้วยฟังก์ชัน

    // *!*โค้ดที่เรียกใช้ฟังก์ชัน*/!*
    let elem = createElement();
    setHandler(elem);
    walkAround();
    
    // --- *!*ฟังก์ชันตัวช่วย*/!* ---
    function createElement() {
      ...
    }
    
    function setHandler(elem) {
      ...
    }
    
    function walkAround() {
      ...
    }
  3. ผสมสองแบบเข้าด้วยกัน โดยประกาศฟังก์ชันตรงจุดที่เรียกใช้ครั้งแรก

ส่วนใหญ่นิยมใช้แบบที่ 2

เหตุผลคือเวลาอ่านโค้ด เรามักอยากรู้ก่อนว่า โค้ดทำอะไร ถ้าวางโค้ดหลักไว้ก่อน ภาพรวมจะชัดตั้งแต่ต้น และอาจไม่ต้องไล่อ่านฟังก์ชันด้านล่างเลย โดยเฉพาะเมื่อชื่อฟังก์ชันบอกหน้าที่ชัดอยู่แล้ว

คู่มือสไตล์

คู่มือสไตล์รวบรวมกฎทั่วไปเรื่องวิธีเขียนโค้ด เช่น ใช้เครื่องหมายคำพูดแบบไหน เยื้องกี่ช่อง หรือกำหนดความยาวบรรทัดสูงสุดเท่าไร ทั้งหมดเป็นรายละเอียดเล็ก ๆ ที่มีอยู่มากมาย

เมื่อทุกคนในทีมใช้คู่มือสไตล์เดียวกัน โค้ดจะมีรูปแบบสม่ำเสมอไม่ว่าใครเป็นคนเขียน

ทีมจะเขียนคู่มือขึ้นมาเองก็ได้ แต่โดยทั่วไปไม่จำเป็น เพราะมีคู่มือให้เลือกใช้อยู่แล้วหลายชุด

ตัวอย่างคู่มือที่ได้รับความนิยม:

ถ้าเพิ่งเริ่มเขียนโปรแกรม ให้เริ่มจากภาพสรุปต้นบทก่อน แล้วค่อยเปิดดูคู่มือชุดอื่นเพื่อเก็บแนวคิดเพิ่มและเลือกสไตล์ที่ชอบ

Linter ช่วยตรวจอัตโนมัติ

Linter คือเครื่องมือที่ตรวจสไตล์ของโค้ดให้อัตโนมัติ พร้อมแนะนำจุดที่ควรแก้

ข้อดีคือระหว่างตรวจสไตล์ linter อาจเจอบั๊กบางอย่างด้วย เช่น พิมพ์ชื่อตัวแปรหรือฟังก์ชันผิด จึงควรใช้ linter แม้ไม่ได้ตั้งใจยึด "สไตล์การเขียนโค้ด" ชุดใดเป็นพิเศษ

ตัวอย่าง linter ที่รู้จักกันแพร่หลาย:

  • JSLint — เป็นหนึ่งใน linter รุ่นแรก ๆ
  • JSHint — ตั้งค่าได้มากกว่า JSLint
  • ESLint — น่าจะใหม่ที่สุดในกลุ่มนี้

ทุกตัวตรวจโค้ดได้ดี ส่วนผู้เขียนใช้ ESLint

Linter ส่วนใหญ่เชื่อมต่อกับ editor ยอดนิยมได้ เพียงเปิดใช้ปลั๊กอินใน editor แล้วกำหนดสไตล์ที่ต้องการ

ถ้าจะใช้ ESLint ให้ทำตามขั้นตอนนี้:

  1. ติดตั้ง Node.js
  2. ติดตั้ง ESLint ด้วยคำสั่ง npm install -g eslint (npm คือตัวติดตั้งแพ็กเกจของ JavaScript)
  3. สร้างไฟล์ตั้งค่าชื่อ .eslintrc ไว้ที่โฟลเดอร์หลักของโปรเจกต์ JavaScript (โฟลเดอร์ที่เก็บไฟล์ทั้งหมด)
  4. ติดตั้งหรือเปิดใช้ปลั๊กอิน 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

สรุป

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

เมื่อคิดว่าจะเขียนโค้ดให้ "ดีขึ้น" อย่างไร ให้ถามว่า "อะไรทำให้โค้ดอ่านและเข้าใจง่ายขึ้น?" และ "อะไรช่วยให้เราเลี่ยงข้อผิดพลาดได้?" สองคำถามนี้คือสิ่งสำคัญในการเลือกและพูดคุยเรื่องสไตล์การเขียนโค้ด

อ่านคู่มือสไตล์ยอดนิยมเป็นระยะ ๆ จะช่วยให้ตามแนวคิดเรื่องสไตล์โค้ดและแนวปฏิบัติที่ดีได้ทัน