JavaScript Coding Convention - รูปแบบมาตรฐานของการเขียน JavaScript

ขอบคุณ Douglas Crockford ครับผม
Credit: Douglas Crockford - http://javascript.crockford.com/code.html

JavaScript File
- ชื่อ file จะต้องลงท้ายด้วย .js เสมอ
- ไม่ควร embed JavaScript code ใน HTML ถ้า code นั้นไม่ได้ใช้กับ file นั้น file เดียว
เพื่อประโยชน์ในการใช้งาน cache ให้เต็มที่
- tag script ควรจะอยู่ท้ายที่สุดเท่าที่จะทำได้ เพื่อไม่ให้ไปรบกวนการโหลด static element ของหน้านั้น

Indentation - การย่อหน้า
Crockford แนะนำให้ใช้ 4 space แต่ผมใช้แค่ 2 ครับ
สิ่งที่สำคัญกว่าจำนวน space ที่ใช้ก็คือ "ห้ามใช้ tab" เพราะ ณ ปัจจุบัน โลกเรายังไม่มีมาตรฐานการวาง tabstop
ซึ่งจะทำให้ code เละโดยไม่จำเป็นถ้าเปิดด้วย editor ที่ tabstop ไม่ตรงกับของเรา

การใช้ space ทำให้ code อ่านง่าย แม้ว่าจะทำให้ file ใหญ่ขึ้น แต่เราสามารถใช้ minification
หรือ obfuscation ช่วยลดขนาดได้

Line Length - ความยาวบรรทัด
ควรจำกัดความยาวของแต่ละบรรทัดอยู่ที่ไม่เกิน 80 ตัวอักษร
แต่ถ้าบรรทัดนั้นยาวกว่า 80 ตัวอักษร ให้ตัดบรรทัดหลัง operator เช่น comma (,)
และบรรทัดที่ตัดลงมานั้นให้ย่อหน้าเพิ่มขึ้นจากปรกติอีกเท่าหนึ่ง (Crockford = 8, ผม = 4)

Comments
จงเขียน comment เพื่อประโยชน์ในการกลับมาอ่าน code ในภายหลัง
ทั้งตัวเราเองและผู้อื่น ใส่มุขขำ ๆ ไปบ้างก็ได้ไม่ว่ากัน

เราจะต้อง update comment ที่เกี่ยวข้องกับ code ที่แก้ทันที
มิเช่นนั้น comment ที่ไม่ update นั้นจะทำให้เกิดความสับสนเพิ่มขึ้น

ไม่ควรเขียน comment บอกสิ่งที่เห็นอย่างโจ่งแจ้ง เช่น

i = 0; // Set i to zero

แบบนี้ไม่ต้องบอกก็รู้น่า -- Obvious comment is garbage!

โดยปรกติให้ใช้ Line comment (//) ส่วน Block comment (/* */) จะใช้ในการเขียน document
แบบเป็นทางการ และเอาไว้สำหรับการข้ามการทำงานบางส่วนของ code

Variable Declarations - การประกาศตัวแปร
ถึงแม้ว่า JavaScript จะไม่ได้บังคับให้ประกาศตัวแปรก่อนใช้งาน
แต่เรา "ควร" จะประกาศตัวแปรทุกตัวก่อนใช้งานเสมอ
เพื่อให้โปรแกรมอ่านง่ายและสามารถตรวจสอบตัวแปรที่เป็น global โดยมิได้ตั้งใจ (implied global) ได้ง่ายขึ้น

ให้ประกาศตัวแปรในส่วนแรกของ function

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

    var currentEntry; // currently selected table entry
    var level;        // indentation level
    var size;         // size of table

JavaScript ไม่มี block scope จึงไม่ควรประกาศตัวแปรใน block ใด ๆ
ให้ประกาศที่เดียวที่ต้น function เท่านั้น

ควรหลีกเลี่ยงการใช้งานตัวแปร global ให้มากที่สุด
และ"ไม่"ควรใช้ตัวแปรที่เป็น global แบบไม่ได้ตั้งใจ (implied global)

ถ้าให้แปลเป็นภาษาไทยจริง ๆ implied global น่าจะแปลว่า "ตัวแปรระดับโลกแบบปริยาย" เง้อ!

Function Declarations - การประกาศ function
ควรประกาศ function ก่อนใช้งานเสมอ
function ภายใน function ให้ประกาศหลังจากประกาศตัวแปร

ให้พิมพ์ ( ติดกับชื่อ function และให้มีช่องว่าง 1 ช่องระหว่าง ) และ {

ส่วน } ให้ย่อหน้าให้เสมอกับบรรทัดที่ประกาศ function

    function outer(c, d) {
        var e = c * d;

        function inner(a, b) {
            return (e * a) + b;
        }

        return inner(0, 1);
    }

ตัวอย่างเกือบเป็น closure แล้วนะเนี่ย

ถ้าไม่จำเป็นต้องมีชื่อ function ให้เว้นวรรคระหว่างคำว่า function กับ ( จำนวน 1 ช่อง
เพื่อป้องกันความสับสน

ควรใช้ global function ให้"น้อยที่สุด"เท่าที่จะทำได้

Names - การตั้งชื่อ
ชื่อต่าง ๆ ควรประกอบด้วยตัวอักษรเหล่านี้ A-Z, a-z, 0-9 และ _ (underscore)

ชื่อตัวแปรและ function ควรเริ่มด้วยตัวอักษรเล็ก

Constructor function ที่ใช้ new เพื่อสร้าง Object ให้ขึ้นต้นด้วยตัวอักษรใหญ่
เพื่อเป็นการเตือนมิใช้เรียกใช้โดยไม่ผ่าน new

ตัวแปร Global ควรใช้ตัวอักษรใหญ่ทั้งหมด

Statements
Simple Statement
แต่ละบรรทัดควรมีแค่ statement เดียว และให้ต่อท้าย statement ด้วย ; เสมอ
และอย่าลืมใส่ ; ให้กับ var statement ที่ assign ค่า function หรือตัวแปรที่ยาว ๆ ด้วย
โดยมาก statement แบบนี้มักจะยาวจนเราลืมไปว่ามันเป็น assignment

Compound Statements
Compound statements คือ statement ที่ประกอบด้วยหลาย ๆ statement ที่อยู่ใน { }
- statement ที่อยู่ภายในให้ indent ด้วย
- ให้ { อยู่ท้ายบรรทัดที่จะเริ่ม compound statement
- ให้ } อยู่ตรงกับ indent ของบรรทัดที่เริ่ม compound statement
- ควรใช้ { } กับทุก statement โดยเฉพาะอย่างยิ่ง single statment ที่อยู่ใน control structure
เช่น for, if เพื่อป้องกันการเพิ่ม statement แล้วลืมเปลี่ยนเป็น compound statement

Labels
ไม่จำเป็นต้องใช้

return Statement
ไม่ควรใช้ ( ) รอบค่าที่ return และการเขียนค่าที่ return ควรจะเริ่มที่บรรทัดเดียวกับคำสั่ง return
เพื่อป้องกันการตัดบรรทัดอัตโนมัติ

if Statement
ตามนี้เลยครับ

    if (condition) {
        statements
    }
    
    if (condition) {
        statements
    } else {
        statements
    }
    
    if (condition) {
        statements
    } else if (condition) {
        statements
    } else {
        statements
    } 

for Statement

    for (initialization; condition; update) {
        statements
    }

    for (variable in object) {
        if (filter) {
            statements
        }
    }

แบบแรกใช้กับ array หรือ loop ที่รู้จำนวนครั้งในการวน

แบบที่สองให้ใช้กับ object โดยให้ใช้ method hasOwnProperty ในการตรวจสอบว่า object มี property นี้อยู่หรือไม่ ดังนี้

    for (variable in object) {
        if (object.hasOwnProperty(variable)) {
            statements
        }
    }

while Statement

    while (condition) {
        statements
    }

do Statement

    do {
        statements
    } while (condition);

do statement จะต้องปิดด้วย ; เสมอ

switch Statement

    switch (expression) {
    case expression:
        statements
    default:
        statements
    }

ให้จบแต่ละ case ด้วย break, return หรือ throw ไม่ควรปล่อยผ่าน

try Statement

    try {
        statements
    } catch (variable) {
        statements
    }

    try {
        statements
    } catch (variable) {
        statements
    } finally {
        statements
    }

continue Statement
ควรหลีกเลี่ยงการใช้ continue

with Statement
ไม่ควรใช้ with

Whitespace
ใช้บรรทัดว่างเพื่อแบ่งกลุ่ม code ที่เกี่ยวข้อง

การใช้ช่องว่างในกรณีต่อไปนี้
- ระหว่าง keyword และ ( เช่น

while (true) {

- ไม่ควรเว้นช่องว่างระหว่างชื่อ function และ ( เพื่อการแยกแยะระหว่าง keyword และการเรียก function
- ให้เว้นช่องว่างด้านหน้าและหลัง binary operator ยกเว้น . (period) ( และ [
- ไม่เว้นช่องว่างระหว่าง unary operator และ operand ยกเว้น unary operator นั้นเป็นคำ เช่น typeof
- เว้นวรรคหลัง ; ใน ( ) หลัง for
- เว้นวรรคหลัง , ทุกครั้ง

อื่น ๆ
{ } และ [ ]
ใช้ { } แทน new Object()
และใช้ [ ] แทน new Array()

ให้ใช้ array ในกรณีที่เรียกใช้ member ด้วยตัวเลข
ใช้ object ในกรณีที่ member เป็นตัวอักษร

, (comma) Operator
ไม่ควรใช้ comma operator ยกเว้นใน control part ของ for statement
เนื่องจากการมีหลาย statement ในบรรทัดเดียวอาจทำให้งงเวลาอ่านได้

Block Scope
JavaScript ไม่มี scope สำหรับ block
ดังนั้นจึงไม่ควรใช้ block นอกเหนือจาก compound statement

Assignment Expressions
ควรหลีกเลี่ยงการ assign ค่าในส่วนการตรวจสอบเงื่อนไข เช่นถ้าเราเขียนแบบนี้

if (a = b) {

อาจจะทำให้คนอ่านงงว่าจริง ๆ แล้วตรงนี้ถูกแล้ว หรือว่าควรจะเป็นแบบนี้มากกว่า

if (a == b) {

=== and !== Operators
โดยปรกติควรที่จะใช้ === และ !== เนื่องจาก === และ !== จะตรวจชนิดของข้อมูลด้วย

eval is Evil
หลีกเลี่ยงการใช้ eval จะเป็นการดีที่สุด

eval มี alias ด้วยนะ นั่นคือ
เราไม่ควรใช้ Function constructor บรรทัดนี้ผมยังงงอยู่นะ?
และไม่ควรส่ง string ไปยัง setTimeout และ setInterval แต่ตรงนี้เข้าใจ

Credit: Douglas Crockford - http://javascript.crockford.com/code.html