JavaScript จะใส่ semicolon (;) หรือไม่ใส่ก็ได้ ไม่ทำให้โค้ดดีขึ้นหรือแย่ลง แทบไม่มีปัญหาอะไรแล้ว ขึ้นอยู่ความชอบส่วนตัวมากกว่า

จากโพลในกลุ่ม สมาคมโปรแกรมเมอร์ไทย:

ปกติเขียน Javascript ใส่ semicolon กันไหมครับ
และเพราะอะไรถึง ใส่/ไม่ใส่ ครับ

Automatic semicolon insertion ในภาษา JavaScript

ในภาษา JavaScript มีระบบ ASI (Automatic semicolon insertion) ซึ่งทำให้เวลาเขียนโค้ด JavaScript จะใส่ ; หรือไม่ใส่ก็ได้ จึงเป็นคำถามที่ว่า ‘ตกลงควรใส่หรือไม่ควรใส่?’ ซึ่งเป็นประเด็นที่ถกเถียงกันอย่างหนัก และหลาย คนก็มาโหวตและให้เหตุผลกัน บางเหตุผลก็สมเหตุสมผลดี แต่บางเหตุผลก็ไม่ถูกต้องครับ

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

ส่วนตัวผมไม่อะไรมากเรื่อง ; จะใส่หรือไม่ใส่ก็ได้ (ปกติผมจะไม่ใส่ แต่ถ้าผมไปทำงานในโปรเจกต์ที่เขาใส่ ; กัน ผมก็จะใส่ด้วย ไม่มีปัญหาอะไร)

แต่ที่มาเขียนโพสต์นี้เพราะคิดว่า เรื่องยิบย่อยแบบนี้ ควรจะเลิกเถียงกันไปตั้งนานแล้ว เอาเวลาไปทำอย่างอื่นกันดีกว่า~

เหตุผลที่ไม่ถูกต้อง

ถ้าอ้างด้วยเหตุผลพวกนี้ เถียงยังไงก็ไม่จบครับ

“ควรใส่ ; เพราะเสี่ยงเขียนโค้ดผิดน้อยกว่า”

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

คนที่ใส่ ; เพราะคิดว่าไม่ควรพึ่งระบบ ASI (Automatic semicolon insertion) ต้องระวังเคสนี้ครับ:

var useSemicolons = function ()
{
  var numberOfBugs = 0;
  return
  {
    bugs: numberOfBugs
  };
};

เคสนี้ คนที่ใส่ ; จนชินอาจจะอ่านโค้ดข้างบนแล้วคิดว่า ฟังก์ชันนี้ควรจะให้ผลลัพธ์เป็น { bugs: 0 } แต่มันกลับคืนค่าเป็น undefined ออกมาแทนครับ ฟังก์ชันนี้พังเพราะว่าคำสั่ง return มันอยู่ด้วยตัวมันเองได้ ทำให้ JavaScript อ่านโค้ดเป็นแบบนี้:

var useSemicolons = function () {
  var numberOfBugs = 0;
  return;
  {
    bugs:
      numberOfBugs;
  }
};

ซึ่งหากปกติไม่ได้ใส่ ; อยู่แล้ว ก็จะไม่เจอปัญหานี้ เพราะจะเห็นได้ทันทีว่าเราเขียนคำสั่ง return เปล่า

“ไม่ควรใส่ ; เพราะเสี่ยงเขียนโค้ดผิดน้อยกว่า”

คนที่ไม่ใส่ ; เพราะคิดว่าไม่จำเป็น จะต้องระวังเคสนี้ครับ:

var n = 10
(a || b).x = n

เคสนี้จะพัง เพราะบรรทัดที่สองขึ้นต้นด้วย ( จึงสามารถเอาไปต่อกับบรรทัดแรกได้ JavaScript เลยรวมสองบรรทัดเข้าด้วยกัน ทำให้โค้ดข้างบนกลายเป็นแบบนี้:

var n = 10(a || b).x = n;

ซึ่งถ้าหากใส่ ; เป็นประจำ ก็จะไม่น่าเจอปัญหานี้ครับ

จะเห็นได้ว่า ไม่ว่าใส่หรือไม่ใส่ ก็เสี่ยงทั้งคู่ — ซึ่งปัจจุบัน ESLint เครื่องมือที่ช่วยตรวจโค้ด JavaScript (ใช้กับ TypeScript ก็ได้นะ) ก็สามารถป้องกันเราจากสองเคสนี้ได้ครับ

“ควรใส่ เพราะ Tools บางตัว (เช่นพวก Minifier) จะทำงานผิดพลาดถ้าไม่ใส่”

เหตุผลนี้อาจจะจริงในหลายปีก่อน แต่เดี๋ยวนี้ไม่จริงแล้วครับ

ภาษา JavaScript เป็นภาษาตามมาตรฐานของ ECMA-262 ซึ่งกำหนดวิธีอ่านโค้ดไว้อย่างชัดเจนตั้งแต่เวอร์ชั่นแรก (1997) แล้วครับ โดย Tools ทุกตัวที่อ่านโค้ด JavaScript ตามมาตรฐาน จะสามารถอ่านโค้ดที่ไม่ใส่ ; ได้อย่างไม่มีปัญหาเลยครับ

ต่อให้ย้อนกลับไปก่อนที่ภาษา JavaScript จะกลายเป็นมาตรฐาน กฏของการใส่ ; อัตโนมัติ ก็ถูกกำหนดไว้ชัดเจนตั้งแต่แรก แล้วครับ ขนาด IE5 ยังอ่านได้อย่างไม่มีปัญหาเลย

ตอนนี้ Tools เกือบทุกตัวในที่คนมักใช้กัน ก็สามารถอ่านได้อย่างไม่มีปัญหาแล้วครับ (ส่วนตัวไหนที่อ่านไม่ได้ ถือว่าไม่ได้มาตรฐานและไม่ควรใช้ครับ)

“ควรใส่เพื่อให้มันถูกต้องตามหลักภาษา”

อย่างที่กล่าวไปในหัวข้อก่อนหน้า — ภาษา JavaScript มีกฏของการใส่ ; ที่ชัดเจน ถ้าทำความเข้าใจมันก็จะรู้ว่า JavaScript จะขึ้นประโยคใหม่ตรงไหน

โค้ดที่ไม่ใส่ ; จึงไม่ผิดกฏแต่อย่างใด

“ควรใส่ จะได้รู้ว่าแต่ละคำสั่งจบตรงไหน” “ควรใส่/ไม่ควรใส่ จะได้อ่านง่าย”

คำสั่งส่วนมากก็จบที่ท้ายบรรทัดอยู่แล้ว การใส่หรือไม่ใส่ ; จึงแทบไม่มีผลต่อความอ่านง่ายเลย:

try {
  var start = now()
  return f.apply(this, arguments)
} finally {
  var finish = now()
  var stat = stats[title] || (stats[title] = new Stat(title))
  stat.push(finish - start)
}

นอกจากนี้ภาษาใหม่ เช่น Kotlin และ Swift ก็ไม่ต้องใส่ ; และไม่เคยเจอใครบ่นว่าภาษาพวกนี้มันอ่านยากเพราะไม่มี ; เลย

แต่การจัดเรียงบรรทัดต่างหากที่มีผลต่อความอ่านง่าย:

try { var start = now(); return f.apply(this, arguments); } finally
{ var finish = now(); var stat = stats[title] ||
                                (stats[title] = new Stat(title));stat.push(finish - start); }

“ควรใส่ จะได้เป็นระเบียบ” “ไม่ควรใส่ เพราะไม่ใส่แล้วสวยกว่า”

คำว่าสวยหรือเป็นระเบียบ ขึ้นอยู่กับคนดู ดังสำนวนที่ว่า “Beauty is in the eye of the beholder” ครับ ไม่ต่างอะไรกับบางเพลงที่ผมชอบ แต่คนอื่นไม่ชอบ

ดังนั้นเราชอบอะไรหรือมองว่าอะไรสวยหรือเป็นระเบียบกว่า ก็ไม่ได้แปลว่าคนอื่นควรจะเห็นตรงกับเราครับ

เหตุผลที่ใช้ได้

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

ส่วนเหตุผลพวกนี้ โอเคครับ:

  • ใส่ เพราะปกติเขียนโค้ดตามกฏของ Airbnb
  • ใส่ เพราะปกติเขียนโค้ดตามกฏของ Google
  • ไม่ใส่ เพราะปกติใช้ JavaScript Standard Style
  • ใส่ เพราะปกติเขียน PHP / Java / C++ / … ด้วย จะได้เหมือน กัน
  • ไม่ใส่ เพราะปกติเขียน Ruby / Python / Go / … ด้วย จะได้เหมือน กัน
  • ใส่ เพราะชินกับภาษา PHP / Java / C++ / …
  • ไม่ใส่ เพราะเกะกะลูกตา
  • ถ้าเกิดทำเว็บด้วย PHP ก็ควรใส่ โค้ดจะได้ดูเหมือน กัน
  • ถ้าเกิดทำเว็บด้วย Python ก็ไม่ควรใส่ โค้ดจะได้ดูเหมือน กัน
  • ใส่/ไม่ใส่ เพราะโปรเจกต์ที่ทำงานเขาใส่/ไม่ใส่กัน
  • ใส่ เพราะชอบใส่ / ไม่ใส่ เพราะไม่ชอบใส่
  • ไม่ใส่ เพราะขี้เกียจใส่
  • จะใส่หรือไม่ใส่ก็ได้ แต่ถ้าใส่ก็ควรใส่ไปเลย หรือถ้าไม่ใส่ก็ไม่ควรใส่ไปเลย ให้มันเหมือน กันทั้งโปรเจกต์
  • (ส่วนตัวของผมเหตุผลนี้) ไม่ใส่ เพราะถ้าเกิดใส่แล้วเวลาลืมจะโดน ESLint เตือน บางทีเขียนโค้ดยังไม่จบบรรทัดมันก็เตือนแล้วมันรำคาญ แต่ถ้าเกิดไม่ใส่ผมไม่ค่อยเจอปัญหานี้ เพราะปกติไม่ลืมไม่ใส่

อ่านเพิ่มเติม