for-in Loops
for-in ลูปมักจะใช้กับการวนใน object ซึ่งเราจะเรียกการวนแบบนี้ว่า enumeration ในเชิงเทคนิคแล้วคุณสามารถใช้ฟังก์ชั่นนี้กับ array ก็ได้เพราะ javascript ก็มอง array คือ object อยู่ดีแต่ไม่แนะนำ มันอาจจะนำมาซึ่งข้อผิดพลาดได้ อาจจะงงยกตัวอย่าง ถ้า array ตัวนั้นมี augment เป็น function อยู่ในตัวมัน นอกจากนี้มันยังไม่สามารถยืนยันได้ชัวร์ๆเลยว่าจะเอาค่าแต่ละตัวออกมาได้จริงๆถ้าหากเราใช้ for-in
ฉะนั้นเราจึงควรจะใช้ for loop สำหรับ array ธรรมดา และ for-in loop สำหรับ object
ในการวนลูปของ object ที่เรากำลังพูดถึงนี้มี method หนึ่งที่เรียกว่าเป็นพระเอกเลยคือ hasOwnProperty() ซึ่งมันจะตรวจจับว่าตอนกำลังวนหาค่าข้างใน object นั้นมีตัวไหนไม่ใช่ค่าของ object ตัวนั้นหรือเปล่าพูดแล้วอาจจะงงไปดูตัวอย่างกันเลยดีกว่า
// the object var man = { hands: 2, legs: 2, heads: 1 }; // somewhere else in the code // a method was added to all objects if (typeof Object.prototype.clone === "undefined") { Object.prototype.clone = function () {}; }
จากตัวอย่างที่กำหนด เรากำหนด object man ชื่อมาโดยมีค่าข้างในเป็น hands, legs, heads อย่างที่เห็นแล้วบรรทัดต่อมาเนี้ยมีการเติม method clone ให้กับทุก object ย้ำนะครับ ทุก object นั่นหมายถึงตัว object ที่ชื่อ man ของเราด้วย ซึ่งหมายความว่าปัจจุบันตัว object man ของเราเป็นอย่างนี้ครับ
// the object var man = { hands: 2, legs: 2, heads: 1, clone: function() {} };
ซึ่งการประกาศอย่างนี้มันดีที่ว่า object ทุกตัวอาจจะต้องมี Method นี้ในการทำอะไรบ้างอย่าง แต่เวลาที่เราวนลูปนั้นค่าของ close จะถูกนับว่าเป็นหนึ่งในค่าที่ถูกวนออกมาด้วย เพราะฉะนั้นเราต้องการเพียงค่าของ hands, legs, heads แต่ไม่ต้องการให้มันวน clone ออกมาจึงใช้คำสั่งในการเช็คว่าค่า property ตัวนี้เป็นของ object นั้นๆที่กำลังวนอยู่หรือเปล่า ไปดูโค้ดกันครับ
// 1. // for-in loop for (var i in man) { if (man.hasOwnProperty(i)) { // filter console.log(i, ":", man[i]); } } /* result in the console hands : 2 legs : 2 heads : 1 */ // 2. // antipattern: // for-in loop without checking hasOwnProperty() for (var i in man) { console.log(i, ":", man[i]); } /* result in the console hands : 2 legs : 2 heads : 1 clone: function() */
เห็นไหมครับถ้าเราวนไม่ดูตาม้าตาเรือนั้นเราจะได้ค่า clone ติดมาด้วยซึ่งเราไม่ต้องการต่อไปเราจะ optimize ได้อีกนิดในการเขียนอะไรที่ยาวๆสำหรับการเช็คค่า hasOwnProperty ได้ดังนี้
var i, hasOwn = Object.prototype.hasOwnProperty; for (i in man) { if (hasOwn.call(man, i)) { // filter console.log(i, ":", man[i]); } }
และเราสามารถทำให้อ่านง่าย ( readable ) ได้อีกโดยการเอามาอยู่บรรทัดเดียวกันอย่างนี้ครับ
// Warning: doesn't pass JSLint var i, hasOwn = Object.prototype.hasOwnProperty; for (i in man) if (hasOwn.call(man, i)) { // filter console.log(i, ":", man[i]); }
(Not) Augmenting Built-in Prototypes
อย่างที่เห็นว่าเราสามารถสร้างหรือแอดค่าใหม่ๆ ไม่ว่าจะเป็น function หรือ object ลงใน object อีกทีเช่นเดียวกับการแอด method clone เข้าไปใน object man ซึ่งดูแล้วมีประโยชน์มากแต่บางครั้ง … มันเกินพอดี
เพราะการที่เราใช้มันจนเกินความจำเป็นไม่ว่าเราจะใส่อะไรเข้าไปใน constructor ( เวลาเรียกใช้แล้วมีค่ารอไว้ก่อน ) เช่น Object(), Array(), or Function() มันทำให้การดูแลโค้ดภายหลังลำบาก ทำไม ? เพราะว่ามันทำให้โค้ดของเราคาดเดายาก ถ้าหากมีเพื่อนและคนในทีมเราทำงานต่อจากเรา เขาอาจจะคาดหวังว่าตัวเขาจะใส่เพิ่มพวก Method ของเขาเเพื่อให้สอดคล้องกับโค้ดของเราและไม่ได้ต้องการการเพิ่มหรือแอดอะไรบางอย่างจากเรา เช่น เพื่อนเราอาจจะมาเขียนฟังก์ชั่น clone ใส่มาในภายหลังที่ทำงานต่อจากเรา แต่เรากลับหวังดีทำไปก่อนไม่ได้ตกลงกันไว้อย่างนี้เป็นต้น หรือหากตกลงกันไว้แล้วก็ไม่ควรทำเกินขอบเขตของโค้ดเราครับ
นอกจากนี้การที่เราใส่ค่า property เข้าไปใน object มันอาจจะไปแสดงผลตอนเพื่อนเราเอาไปวนลูปหาค่า ถ้าหากเพื่อนเราไม่รู้วิธีใช้ hasOwnProperty() มาก่อนเขาจะเกิดความสับสนว่าอะไรฟ่ะ ตูวนแล้วค่านี้มาไง
เพราะฉะนั้นมันจะดีที่สุดถ้าคุณไม่สร้างอะไรแปลกๆเข้าไปใน prototype เราเรียกว่า built-in prototype แต่ ! คุณสามารถมีข้อยกเว้นได้ก็ต่อเมื่อเงื่อนไขดังนี้
- ถ้าหาก ECMAScript versions ( คล้ายๆพวกองค์กรในการกำหนดมาตราฐานว่าจะใช้ function อะไรเข้าไปในตัวหลักของ javascript พวกฟังก์ชั่นพื้นฐานนั่นแหละ ) จะทำ method ใหม่ๆที่จะ build-in เข้าไปในในตัว javascript คุณสร้างฟังก์ชั่นแบบนั้นรอได้เลย
- คุณทำการเช็ดแล้วว่าถ้าหากตัว property หรือ method ที่คุณต้องการใช้มันหาไม่เจอ ซึ่งอาจจะมีการใช้แล้วในบางส่วนของโค้ดของคุณหรือมันเป็นส่วนหนึ่งของ JavaScript engine ที่ตัว Browser ของคุณ support ก็เขียนได้เลย
- คุณมีการตกลงกับทีมแล้วว่าจะสร้าง built-in ตัวนี้ๆนั้นๆนะ แล้วสามารถเรียกใช้ได้ตกลงกับทีมให้ดีก็โอเคเขียนเลย
ถ้าตรงตามเงื่อนไข 3 อย่างด้านบน คุณสามารถสร้าง property หรือ function เข้ากับตัว prototype ได้เลยครับโดยสามารถทำตาม pattern นี้ได้
if (typeof Object.protoype.myMethod !== "function") { Object.protoype.myMethod = function () { // implementation... }; }
ปล. การแอดอย่างนี้คือการแอดเข้าไปทุก object หมายถึงเป็นส่วนหนึ่งของ javascript เลยนะครับ
switch Pattern
ว่ากันเรื่อง switch ต่อครับเราสามารถทำให้มันอ่านง่ายและดูเป็นระเบียบเรามาดูกันเลยดีกว่า
var inspect_me = 0, result = ''; switch (inspect_me) { case 0: result = "zero"; break; case 1: result = "one"; break; default: result = "unknown"; }
การเขียนอย่างนี้ดียังไง มันดีอย่างนี้ครับ
- มีการจัดตำแหน่งของ case และ switch ตามกฎการ indentation
- indent ( กด tab ) ในส่วนโค้ดข้างใน case
- จบด้วย break;
- หลีกเลี่ยงการไม่ใช้ break แต่ถ้าหากคุณมั่นใจว่ามีการพูดคุยกับทีมแล้วหรือการเขียนโค้ดของเราอธิบายได้ดีแล้วเพราะว่า หากใครมาอ่านโค้ดของคุณจะงงเพราะมันจะดูคล้ายๆกับการเขียนให้ error
- เขียน default ทุกครั้งจะได้รู้ว่ามีอันนี้ที่ไม่ตรงกับ case ที่เราคาดหวัง
Avoiding Implied Typecasting
Javascript จะทำการเปลี่ยน type ของตัวแปรให้อัตโนมัติเมื่อมีการ compare ใน if เช่น ถ้าหากเราต้องการเปรียบเทียบในเงื่อนไขว่าเป็น false การใส่การเปรียบเทียบเหล่านี้ให้ผลเป็น false เหมือนกันเช่น false == 0 หรือ “” == 0 return true
การป้องกันการสับสนเวลาเปรียบเทียบให้เป็นอย่างที่เราต้องการให้ใช้ === หรือ !== สำหรับการเปรียบเทียบค่า not เพื่อเช็คว่ามันเหมือนกันทั้งค่าและ type ตัวอย่าง
var zero = 0; if (zero === false) { // not executing because zero is 0, not false } // antipattern if (zero == false) { // this block is executed... }
มีหลายๆที่บอกว่ามันเกินไปที่จะมานั่งเปรียบเทียบค่าแบบทั้งค่าและ type เพราะการใช้แค่ == มันเพียงพอแล้ว ยกตัวอย่างเช่น หากคุณใช้ type ที่คุณรู้อยู่แล้วว่ามันจะ return เป็น string ฉะนั้นมันไม่มีเหตุผลที่ต้องใช้ strict equality ( === ) แต่อย่างไรก็ตามเว็บตรวจการเขียนโค้ด Js หรือเรียกว่า ( JSLint ) จะทำการบอกว่าต้องใช้ === เพราะว่าไม่รู้ว่าการที่เราใช้ == มันจะตั้งใจหรือไม่ตั้งใจ
Avoiding eval()
ถ้าหากคุณลองสังเกตุการใช้ evel() ในโค้ดของคุณจำไว้เลยว่ามีคำกล่าวที่ว่า “eval() is evil.” เพราะไอ้เจ้าฟังก์ชั่นมันจะทำการรันคำสั่ง Javascript แม้ว่าเป็นเพียงแค่ประโยค string ธรรมดาซึ่งหมายถึงหายนะที่จะตามมาหากมีการใช้ eval() อย่างไม่ระวังและก็ไม่มีความจำเป็นต้องใช้เลยในหลายๆกรณี
ตัวอย่างเช่นหากต้องการรันคำสั่งในพวกฟังก์ชั่นอย่าง setTimeout() ให้เราทำอย่างนี้ครับ
// antipatterns setTimeout("myFunc()", 1000); setTimeout("myFunc(1, 2, 3)", 1000); // preferred setTimeout(myFunc, 1000); setTimeout(function () { myFunc(1, 2, 3); }, 1000);
แล้วที่น่ากลัวคือตัวฟังก์ชั่น eval() นั้นอาจจะสร้างตัวแปรแบบ global โดยเราไม่รู้ตัวก็ได้เพราะในคำสั่งที่มีบางครั้งอาจจะไม่ได้ประกาศด้วย var อย่างที่เรารู้กันมันทำให้ตัว javascript สร้างตัวแปรแบบ global โดยปริยายนั่นเองหลีกเลี่ยงเป็นดีที่สุดครับ
Number Conversions with parseInt()
ด้วยฟังก์ชั่น parseInt เราสามารถเปลี่ยนจากตัวแปรแบบ string มาเป็น int ได้ เราควรจะใส่ค่า parameter ตัวที่สองด้วยนั่นคือ หน่วยฐานเลขครับ เพราะบางทีมันอาจจะกลับเลขฐานเป็นฐานอื่นได้ถ้าหากมี 0 อยู่ข้างหน้ายกตัวอย่างเช่น
var month = "06", year = "09"; month = parseInt(month, 10); year = parseInt(year, 10);
ตัว year มันอาจจะเป็นเลขฐานแปดได้
Coding Conventions
มันเป็นสิ่งสำคัญมากสำหรับการที่มีข้อตกลงสำหรับการเขียนโค้ด เพราะจะทำให้โค้ดทุกคนสอดคล้องกัน สามารถคาดเดาได้ว่าอะไรเป็นอะไร ใช้อย่างไร และง่ายต่อการอ่านและเข้าใจ หากมีคนใหม่เข้ามาในทีมจะสามารถอ่านและทำความเข้าใจได้จากข้อตกลงที่ทำกันไว้แล้ว เข้าใจได้เร็วก็สูญเสียเวลาเรียนรู้น้อย
หลายๆครั้งในการประชุมหาข้อตกลงว่า ข้อตกลงในการเขียนโค้ดต้องมีอะไร บ้างอย่างไร ไม่ว่าจะเป็นเรื่องเล็กๆอย่าง indentation ควรจะเป็น tab or space ถ้าหากคุณเป็นหนึ่งในคนที่เสนอข้อตกลงในบริษัทหรือทีมควรจะเตรียมตัวที่จะเปิดรับมุมมองอื่นๆ และต้องมั่นใจกับความคิดเห็นต่างๆด้วย เพราะมันสำคัญมากๆจริง
Indentation
การ indent ทำให้อ่านง่ายนักพัฒนาบางคนเสนอการ indent แบบ tab เพราะว่าทุกๆคนสามารถแสดงผล tab ได้และกำหนดได้ด้วยว่าจะเป็น tab ครั้งละกี่ space บางคนก็เสนอว่าควรจะเป็น space ไปเลยโดยเฉพาะมี space = 4 ซึ่งเป็นสากลแต่มันไม่สำคัญหรอกถ้าหากว่าทีมมีข้อตกลงการเขียนโค้ดอย่างไรและยังคงทำตามกันอยู่
แล้วคุณควรจะ indent อะไรบ้าง ? กฎง่ายๆคือ ทุกอย่างที่อยู่ในวงเล็บปีกกา “{}” พวกใน function ก็ใช่ loop ด้วย if , switch และ object property ต่างๆใน object ดูตัวอย่างกัน
function outer(a, b) { var c = 1, d = 2, inner; if (a > b) { inner = function () { return { r: c - d }; }; } else { inner = function () { return { r: c + d }; }; } return inner; }
Curly Braces
คือหมายปีกกาถูกใช้ตลอดแม้แต่ตอนที่มีทางเลือกก็ใช้อยู่ดีในเชิงเทคนิคถ้าคุณมีแค่ 1 statement ใน if หรือ for ปีกกาอาจจะไม่ต้องใช้ก็ได้แต่คุณควรจะใช้ตลอดไม่ว่าจะใส่ได้หรือไม่ใส่ก็ได้มันทำให้โค้ดดูเข้าใจและง่ายต่อการ update
ลองคิดดูคุณมี for loop กับอีก 1 statement คุณสามารถไม่ใส่ปีกกาก็ได้และมันก็ไม่ได้ทำให้ผิด syntax เช่น
// bad practice for (var i = 0; i < 10; i += 1) alert(i);
แต่หลังจากนั้นคุณต้องกลับมาแก้ไขโค้ดเป็น …
// bad practice for (var i = 0; i < 10; i += 1) alert(i); alert(i + " is " + (i % 2 ? "odd" : "even"));
การ alert ของคำสั่งที่บรรทัดที่สองนั้นอยู่นอกการวนลูป เพราะฉะนั้นใส่วงเล็บปีกกาเถอะครับพี่ขอ
// better for (var i = 0; i < 10; i += 1) { alert(i); }
และในทำนองเดียวกันกับการใช้ if else
// bad if (true) alert(1); else alert(2); // better if (true) { alert(1); } else { alert(2); }
Opening Brace Location
เราควรจะใส่ปีกกาให้เท่ากับบรรทัดของเงื่อนไขหรือว่าลงมาอีก 1 บรรทัดกันแน่ ?
จริงๆคือใส่แบบไหนก็ได้แต่มีบางกรณีที่น้อยๆมากๆจำเป็นต้องยกเว้นในคือกรณีของการไม่ใส่ semicolon แล้วตัว javascript ดันใส่ให้เองแบบ auto เช่น
// warning: unexpected return value function func() { return // unreachable code follows { name : "Batman" } }
อย่างนี้มันจะ return เองโดยทันทีไปไม่ถึงบรรทัดของ object ที่มี name เป็น property อยู่ครับเราจึงต้องเขียนแบบนี้
function func() { return { name : "Batman" }; }
White Space
การเว้นวรรคในการโค้ดที่ดีทำตามนี้ครับ
- หลัง semicolon เช่น for (var i = 0; i < 10; i += 1) {…}
- หลังการประกาศค่าแบบ multiple
- หลัง commas ( , ) เช่น var a = [1, 2, 3];
- ก่อนปีกกาถ้าหากเป็นบรรทัดเดียวกัน function () { }
- หลังคำว่า function
และสุดท้ายหลังพวก operation ทั้งหลายเช่น +, -, *, =, <, == เป็นต้น
Naming Conventions
ข้อตกลงในการตั้งชื่อ ควรเลือกชื่อที่ความสอดคล้องระหว่างตัวแปรและฟังก์ชั่นที่คนทั่วไปอ่านแล้วเข้าใจ เช่น ฟังก์ชั่นเกี่ยวกับวันเวลาตัวแปรก็ควรจะเป็นเกี่ยวกับเวลาเช่น min,sec,hour ทำนองเนี้ยอ่ะครับ
Capitalizing Constructors
Javascript ไม่มี class แต่ดันมี constructor function โดยจะถูกใช้เมื่อมีคำสั่งคำว่า new
var adam = new Person();
เพราะว่าตัว constructor ยังคงเป็น function หากคุณต้องการหามันล่ะ ? คงไม่ง่ายใช่ไหมมันมีข้อแนะนำว่าให้ตัวชื่อ function constructor เป็นตัวอักษรตัวใหญ่ตัวแรกก่อน และใช้ตัวเล็กสำหรับพวก function หรือ method ธรรมดาเพื่อจะได้แยกออก
function MyConstructor() {...} function myFunction() {...}
Separating Words
มีสองแบบที่คุ้นเคยอย่างแรกคือ camel คือใช้ชื่อตัวแปรหรือ function สลับตัวเล็กใหญ่เหมือนหลังอูฐอีกแบบคือ snake ใช้ underscore สำหรับแบ่งคำ
ตัวอย่างของ camel เช่น myDate, myFunction
ตัวอย่างของ snake เช่น my_date, old_company_name
Other Naming Patterns
สำหรับพวกค่าคงที่ทั้งหลายนั้นที่จะไม่มีการเปลี่ยนแปลงค่าระหว่างการคำนวนหรือรันคำสั่ง ไม่ใช่ว่าเปลี่ยนค่าไม่ได้เลยนะ แต่หมายถึงในโปรแกรมจะไม่มีการเปลี่ยนให้ใช้ตัวใหญ่หมดทุกตัวเลยเช่น
var PI = 3.14, MAX_WIDTH = 800;
และยังมีการใช้ underscore สำหรับบอกว่าตัวแปรหรือ method ไหนเป็น private ด้วยเช่น
var person = { getName: function () { return this._getFirst() + ' ' + this._getLast(); }, _getFirst: function () { // ... }, _getLast: function () { // ... } };
Writing Comments
การเขียน comment สำหรับโค้ดให้เขียนให้ตัวเองเข้าใจเลยว่ามันทำงานแบบไหนอย่างไร เขียนแบบว่าคนไม่เคยรู้เรื่องเลยมาอ่านแล้วเข้าใจว่ามันทำอะไร ทำงานแบบไหน บ่อยครั้งที่เราต้องมานั่งทำความเข้าใจว่าโค้ดชุดนี้ทำงานแบบไหนอย่างไร มันทำให้เราสูญเสียเวลามากๆ
แต่ก็ไม่ใช่ว่าจะ comment ทุกบรรทัดทุกตัวแปร นั่นก็เวอร์ไปแต่ควรจะมีเอกสารสำหรับ function ใส่อะไรเข้าไปได้อะไรออกมา ให้คิดว่าการ comment คือข้อแนะนำสำหรับคนที่มาอ่านโค้ด เพราะคนที่มาอ่านนั้นต้องการความเข้าใจว่าโค้ดเราทำอะไร ไม่ใช่ว่าต้องการรู้ว่ามีตัวแปรอะไรแต่ละ function ชื่ออะไร เพื่อความรวดเร็วสำหรับคนอื่นเช่น ส่วนตรงนี้เกี่ยวกับเรื่อง edit หากเราไม่ได้จะแก้ไขเกี่ยวกับเรื่อง edit จะได้ข้ามๆไปเลย
Credit
source : http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/
ถ้าคุณชอบบทความในเว็บนี้ และอยากสนับสนุนเรา เพียงแค่คุณสมัครรับข่าวสารด้านล่างจะได้รับสิทธิ์พิเศษก่อนใคร เราสัญญาว่าจะส่งบทความที่เป็นประโยชน์ต่อคุณอย่างแน่นอนครับ