[แปล] Style การเขียน jQuery ทำไมถึงสำคัญ

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

Plugin ส่วนใหญ่ที่ถูกสร้างจาก Programmer และ Designner ที่มาแชร์กับคนอื่นนั้น น่าเสียดายตรงที่ว่า หลายตัวคนเขียนไม่ได้ให้เวลากับการพิจารณากับโค้ดของคนอื่นๆว่าโค้ดตัวไหนมันทำงานดี และตัวไหนทำงานไม่ดี

ขณะที่ Plugin ที่คุณสร้างคุณจะทำอย่างไรกับมันก็ได้ หวังว่าคุณจะพบกับคำแนะนำที่เป็นประโยชน์เมื่อคุณได้สร้าง Plugin ของคุณเองโดยบทความนี้จะมีหัวข้อดังนี้

  1. a few “pro tips”
  2. Playing nice with others
  3. Elements of style
  4. in conclusion

1. a few “pro tips”

เมื่อคุณเริ่มสร้าง Plugin ของคุณ มันมีทางเลือกว่าคุณจะสร้างโค้ดของคุณให้เป็นแนวทางที่ดีไม่ใช่แค่ว่าทำงานได้พอ ( หลายๆคนรวมถึงผมก็ทำแบบนี้ ) แต่ว่าคุณจะ maintain มันได้อย่างไร จำไว้ว่าการที่คุณทำให้ Plugin ของคุณมีประสิทธิภาพที่ดี website หรือ application มันก็จะดีไปด้วย ลองคิดดูสิโค้ดที่อ่านได้จะช่วยให้เรามั่นใจว่าหากอนาคตที่เราต้องกลับมาแก้ไขโค้ดของเราจะไม่เหนื่อย

แลดูมันอาจจะยุ่งยากแต่ถ้าเรายังคงคิดถึงการเรื่องนี้ เราจะหาจุดที่ปรับปรุงได้ครับ

DRY = Don’t repeat yourself

น่าเสียดายที่ว่า เหล่านักพัฒนาได้เรียนรู้เรื่องนี้ยาก โดยทั่วไปหลังจากที่เราเสียเวลาและความพยายามลงไปกับการ maintain โค้ดห่วยๆ ในที่สุดคุณจะได้เรียนรู้หนทางที่ง่ายที่สุดเมื่อคุณ ไม่ทำงานซ้ำ ( Don’t repeat yourself ) สิ่งที่น่าสนใจคือมันช่วยเซฟเวลาและการปวดหัวของคุณในภายหลัง ฉันกำลังจะบอกว่ามันทำอย่างไร

– ถ้าคุณกำลังใช้โค้ดที่ซับซ้อนและหลายๆที่และทำการส่งค่าให้ variable นั้นๆและใช้มัน
– ถ้าคุณกำลังใช้โค้ดที่เป็นแบบ block เดียวกันมากกว่า 1 ที่
– คาดการณ์ว่าโค้ดหรือ function นี้ต้องใช้ตัว argument กี่ตัว
– สร้าง function ชื่อ test , คล้ายๆกับฟังก์ชั่นเดิมที่คุณเคยสร้างไปแล้วล่ะก็

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

// Not great: that's a lot of repeated, not-at-all-DRY code.

$('body')
  .bind( 'click', function(e){ console.log( 'click: ', e.target ); })
  .bind( 'dblclick', function(e){ console.log( 'dblclick: ', e.target ); })
  .bind( 'keydown', function(e){ console.log( 'keydown: ', e.target ); })
  .bind( 'keypress', function(e){ console.log( 'keypress: ', e.target ); })
  .bind( 'keyup', function(e){ console.log( 'keyup: ', e.target ); });

// Good: code is more DRY, which is a huge improvement, but it's not as
// obvious what is being done at first glance.

function myBind( name ) {
  $('body').bind( name, function(e){ console.log( name + ': ', e.target ); })
};

myBind( 'click' );
myBind( 'dblclick' );
myBind( 'keydown' );
myBind( 'keypress' );
myBind( 'keyup' );

// Better: the handler has been generalized to use the event.type property,
// and it's totally obvious what is being done, even at first glance.

function myHandler( e ) {
  console.log( e.type + ': ', e.target );
};

$('body')
  .bind( 'click', myHandler )
  .bind( 'dblclick', myHandler )
  .bind( 'keydown', myHandler )
  .bind( 'keypress', myHandler )
  .bind( 'keyup', myHandler );

// Best: really knowing how the jQuery API works can reduce your
// code's complexity and make it even more readable.

$('body').bind( 'click dblclick keydown keypress keyup', function(e){
  console.log( e.type + ': ', e.target );
});

หากดูตัวอย่างแล้วไม่เข้าใจสามารถถามได้ที่ comment นะครับ

Use the jQuery API

อย่างที่คุณเห็นตัวอย่างข้างบน ไม่มีแบบไหนสำหรับการสร้างที่ดีที่สุดใน jQuery method หรอก อ่านเกี่ยวกับ API Document และตัวอย่างทั้งหมด สำรวจเกี่ยวกับ jQuery source เหมือนกับการสำรวจ Plugin ตัวอื่นนั่นแหละ

มันจะดีกว่าไหมล่ะ เมื่อคุณรู้ว่าแต่ละ API ใช้ทำอะไรบ้าง และความสะอาดและอ่านง่ายของโค้ดของคุณมันจะตามมา นอกจากนี้สำหรับการใช้ jQuery method คุณจะได้กำจัดโค้ดบางส่วนออกไป ผลลัพธ์คือใช้เวลาลงน้อยเมื่อคุณต้องทำการ maintrain

Avoid premature optimization

ขณะทำการ optimize โค้ดมีสิ่งสำคัญคือว่า มันไม่สำคัญว่าทำให้ดค้ดคุณทำงานได้ในเวลาที่กำหนด ปัญหาใหญ่เกี่ยวกับการ optimize บ่อยครั้งคือ มันไม่ได้อ่านง่ายหรือเข้าใจง่ายเหมือนก่อนทำการ optimize

กฎข้อแรกของการ optimize คือ optimize บางสิ่งที่มันต้องการ optimize ก็พอ ถ้าหากทุกอย่างมันทำงานได้ดีและไม่ได้มีปัญหาเรื่อง performance หรือว่า ขนาดไฟล์ใหญ่ คุณอาจจะไม่ต้องการ refactor โค้ดให้ทำงานเร็วขึ้น ขนาดเล็กลงหรือทำให้อ่านง่ายขึ้นแบบ perfect ก็ได้

มี ตัวอย่างโค้ด ที่ฉันทำการ optimize เรียบร้อยแล้ว ผลลัพธ์คือได้ไฟล์เล็กมากเขียนโค้ดสั้น แต่ … อ่านไม่รู้เรื่อง เห็นไหมล่ะ ? ฉันจึงไม่แม้แต่จะตัดสินใจทำการ optimize ไฟล์นั้นเพราะมันทำงานได้ดีอยู่แล้ว

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

Avoid over-avoiding premature optimization

มันเป็นเรื่องที่ยอมรับไม่ได้ที่ว่า “อย่า optimize ก่อน” เพราะมันอาจจะเป็นข้ออ้างในการเขียนโค้ดที่ไม่ดีได้ ฉะนั้นฉันจึงจะมาคุยถึงจุดนี้กัน ขณะที่โค้ดของคุณควรจะอ่านได้ มันไม่ควรที่จะไม่ optimize เลย

ยกตัวอย่าง โดยการ cache jQuery object/chaining method คุณจะสามารถมองเห็นเรื่องประสิทธิภาพโค้ดของคุณ หลายคนพูดถึงหัวข้อนี้ และขณะที่รูปแบบที่ไม่ควรทำ ซึ่งคุณควรจะหลบเลี่ยงมัน ฉันจะแสดงให้เห็นในตัวอย่างต่อไปนี้

// Bad: very slow, and not even remotely DRY.

$('#foo').appendTo( 'body' );
$('#foo').addClass( 'test' );
$('#foo').show();

// Good: the jQuery object reference is "cached" in elem.

var elem = $('#foo')

elem.appendTo( 'body' );
elem.addClass( 'test' );
elem.show();

// Even better: jQuery methods are chained.

$('#foo')
  .appendTo( 'body' )
  .addClass( 'test' )
  .show();

// And you can even combine caching with chaining, which can be especially
// useful in conditionals.

var elem = $('#foo').appendTo( 'body' );

if ( some_condition ) {
  elem.addClass( 'test' );
} else {
  elem.show();
}

สรุปคือ ทำไปด้วยระดับหนึ่งอาจจะไม่ต้องทุกตัวแต่ถ้าหากเห็นแล้วว่ามันพอจะทำได้ ก็ทำเถอะครับ ( เพื่อคนที่คุณรัก )

2. Playing nice with others

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

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

Don’t modify objects you don’t own

ฉันไม่ได้จะลงลึกเกี่ยวกับหัวข้อนี้เพราะว่ามีคนเขียนไว้แล้ว  not modifying objects you don’t own ( ผู้แปล: คงเป็นเรื่องเกี่ยวกับว่าอย่าไปปรับเปลี่ยน object ถ้าเราไม่ใช่เจ้าของ object นั้นครับ )

Declare your variables

ประกาศก่อนทุกครั้ง ใช้คำที่เข้าใจชัดเจน และขอบเขตต้องอยู่ในตัว Plugin ของเรา อย่าให้เกิดอาการ Implicit global ( หรือการเป็นตัวแปรแบบ Global โดยปริยาย หากไม่เข้าใจ เชิญที่นี่เลยจ้า [แปล] สรุปการเขียน Javascript ที่ดี ตอนที่ 1 )

นอกจากนี้ เมื่อพยายามที่จะ maintain code แล้วดันมีตัวแปรแบบ global โดยปริยาย บ่อยครั้งที่มันยากจะรู้ว่าตัวแปรตัวนี้ถูกในที่ไหน อยากมีอนาคตที่สดใสประกาศให้ดีนะครับ

Use a closure

การใส่โค้ดของคุณภายใน function Plugin ของคุณสามารถมี private property and method โดยมันจะต้องไม่ไปทับกับชื่อของพวกตัวแปรภายนอก นอกจากนี้เรื่อง function anonymous และการเรียก execute แบบทันที จะต้องไม่ไปยุ่งกับตัวแปร global อีกด้วยเหมือนกัน

ภายใน function คุณสามารถเรียกใช้มันได้ทันทีและคุณสามารถส่งค่าอะไรก็ได้เข้าไปตามที่คุณต้องการ ในตัวอย่าง top-level Plugin function ทำงานทันทีส่งค่าสำหรับการอ้างอิงโดยการใช้ภายในด้วยเครื่องหมาย “$” ซึ่งทำให้เราสามารถใช้ $ ในการอ้างอิงแบบ jQuery ได้ต่อให้มันจะทำการเปิด noConflict mode ก็ตามที และพยายามทำให้โค้ดอ่านง่าย เอาล่ะดูตัวอย่าง

// Ultra-basic jQuery plugin pattern.
(function($){

  var myPrivateProperty = 1;

  // Call this public method like $.myMethod();
  $.myMethod = function(){
    // Your non-element-specific jQuery method code here.
  };

  // Call this public method like $(elem).myMethod();
  $.fn.myMethod = function(){
    return this.each(function(){
      // Your chainable "jQuery object" method code here.
    });
  };

  function myPrivateMethod(){
    // More code.
  };

})(jQuery);

 

Use namespaces when binding event handlers

เมื่อใช้ bind และ unbind สำหรับการควบคุม event คุณควรจะตั้งชื่อ event ให้น่าเชื่อถือและง่ายต่อการจดจำ มั่นใจด้วยว่าไม่ชนกับใคร

// Bad: this method will unbind every other plugin's 'body' click handlers!
$('body').bind( 'click', handler ); // Bind.
$('body').unbind( 'click' );        // Unbind.

// Good: only the 'body' click handlers bound with the 'yourNamespace'
// namespace are unbound!
$('body').bind( 'click.yourNamespace', handler ); // Bind.
$('body').unbind( 'click.yourNamespace' );        // Unbind.

// Also good: only the 'body' click handlers that reference the 'handler'
// function are unbound (note that since this method requires a function
// reference, it will not work for events bound with an inline anonymous
// function)
$('body').bind( 'click', handler );   // Bind.
$('body').unbind( 'click', handler ); // Unbind.

 

Use unique data names

เหมือนกับการตั้งชื่อ method/event เมื่อทำการจัดเรียง data ใน element ใช้ชื่อที่มันมีเอกลักษณ์เพียงพอ การใช้ชื่อทั่วไปอาจจะเจอการชนกันของโค้ด

// Bad: not a very unique name, may conflict with other code's data.
$('#foo').data( 'text', 'hello world' );

// Good: very unique name, highly unlikely that it will conflict with other
// code's data.
$('#foo').data( 'yourPluginName', 'hello world' );

ถ้าหากคุณต้องการเก็บ data เยอะมากใน element เดียวแล้วละก็ใช้ตัว object เดียวเก็บข้อมูลของเราไว้ต่างหากเลยดีกว่า

function set_data() {
  var data = {
    text: 'hello world',
    awesome: false
  };

  // Store data object all-at-once.
  $('#foo').data( 'yourPluginName', data );

  // Updating data.xyz properties will update the data store as well.
  data.awesome = true;
  data.super_awesome = true;
};

function get_data() {
  var data = $('#foo').data( 'yourPluginName' );
  alert( data.super_awesome );
}

set_data();
get_data(); // Alerts true.

 

3. Elements of style

มันอาจจะเกิดการโต้แย้งกันเพราะว่าการเขียนโค้ด style มันเป็นเรื่องของใครของมัน ไม่มีถูกหรือผิด ประเด็นคือ ต้องสามารถอ่านแล้วเข้าใจได้เลยว่าทำอะไรอย่าง เพราะว่าหากคนอื่นอ่านแล้วต้องการหา Bug หรือต้องการเพิ่ม Feature อะไรใหม่ๆเข้ามาจะได้ไม่ลำบาก

เมื่อคุณกำลังโค้ดอย่าไปกลัวเรื่องการต้องกลับมาดูภายหลังอย่าคิดว่ามันเสียเวลา เพราะโค้ดที่เรากำลังทำมันอยู่นั้น โค้ดมันดีหรือยัง ? มันอ่านแล้วเข้าใจหรือยัง ? มัน make sense ไหม ? ถ้าหากคำตอบว่ายัง … คุณอาจจะไม่สามารถเพิ่มอะไรใหม่ๆได้แน่ภายใน 6 เดือน

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

มีคนเขียนโค้ดสำหรับสื่อสารไว้แล้วดูที่นี่เลย code conventions for javascript

Line length

เชื่อหรือไม่ว่าบางคนยังคงเขียนโค้ดผ่าน shell นั่นแปลว่าหากมีบรรทัดไหนตัวอักษรเกิน 80 ตัวมันจะขึ้นบรรทัดใหม่ซึ่งมันน่าเกียจ ใช่! พวกเขาสามารถจะขยายหน้าจดได้ยาวขึ้นได้แต่คุณควรจะถามตัวเองถ้าหากมีโค้ดบรรทัดไหนที่ต้องยาวว่า 80 ตัวอักษรก็ควรจะจัดการโค้ดให้อยู่บรรทัดใหม่หรือ wrap มันให้ดี

คำแนะนำเรื่องนี้ดูเหมือนจะเข้มงวดสำหรับคุณ แต่ลองฉันเสนอตัวอย่างให้คุณดู code sample คุณอาจจะ scroll บนลงล่างได้ ไปทางขวาได้แต่เชื่อเถอะว่าหากมีส่วนไหนของโค้ดมันหลุดจากหน้าต่างที่คุณกำลังดูโค้ดไปอยู่คุณจะมานั่ง scroll ไปมาหรอ ?

คุณอาจจะ get idea การ scroll แนวนอนใน text editor มันลำบาก ยิงปืนนัดเดียวได้นกสองตัว คือ โค้ดมีจำกัดจำนวนและอ่านง่าย ทุกคน win

Tabs vs. spaces

มีการถกเถียงกันเยอะมากสำหรับเรื่องนี้ หลายคนคงคิดว่า tab มันดูสากลกว่าแต่เวลาดูโค้ดใน browser มันจะกว้างกว่าและบางทีก็ทำให้ดูยุ่งสำหรับบรรทัด comment

ผู้เขียนเสนอว่าใช้ 2 space ก็น่าจะอ่านง่ายกว่าดูตัวอย่างกันเลย

// Tabs (simulated with 8-space indents)
function inArray( elem, array ) {
         if ( array.indexOf ) {
                 return array.indexOf( elem );
         }

         for ( var i = 0, length = array.length; i < length; i++ ) {
                 if ( array[ i ] === elem ) {
                         return i;
                 }
         }

         return -1;
};

// 2-space indents
function inArray( elem, array ) {
   if ( array.indexOf ) {
     return array.indexOf( elem );
   }

   for ( var i = 0, length = array.length; i < length; i++ ) {
     if ( array[ i ] === elem ) {
       return i;
     }
   }

   return -1;
};

แต่จริงๆแล้วก็ขึ้นอยู่กับการตกลงของทีมพัฒนาหรือตัวคุณเองว่าอ่านง่ายไหมถนัดไหม มองง่ายหรือเปล่าเวลาโค้ด

Crowding arguments or code blocks

บางครั้งสั้นแล้วได้ผลดี แต่ไม่ใช่กับการเว้นวรรค ดูตัวอย่างเลย

// Crowded.
function inArray(elem,array) {
   if (array.indexOf) {
     return array.indexOf(elem);
   }
   for (var i=0,length=array.length;i<length;i++) {
     if (array[i]===elem) {
       return i;
     }
   }
   return -1;
};

// Ahh.. a little bit of breathing room.
function inArray( elem, array ) {
   if ( array.indexOf ) {
     return array.indexOf( elem );
   }

   for ( var i = 0, length = array.length; i < length; i++ ) {
     if ( array[ i ] === elem ) {
       return i;
     }
   }

   return -1;
};

เรื่องเว้นวรรคสามารถอ่านได้จากบทความการเขียน javascript ที่ดีได้ครับ

4. In conclusion

ในที่สุดขณะที่มี Plugin มากมายและหลากหลาย style การเขียนโค้ด สิ่งสำคัญคือคุณต้องโฟกัสการพัฒนา style ของคุณเองซึ่งต้องรักษาระหว่างการอ่านง่ายและดูแลง่าย Plugin ของคุณต้องทำงานดีแต่ต้องง่ายต่อการ update และ maintain เพราะว่าคุณคือคนเดียวกันที่จะไปทำการ maintain มันอีกในอนาคตไงล่ะ

คุณควรจะไม่เพียงแต่พิจารณาแค่โค้ดของคุณแต่คุณจะศึกษาจาก jQuery source และพวก Plugin ตัวอื่นด้วย ซึ่งมันจะเป็นอาวุธในการตัดสินใจต่อไปในอนาคตว่าเวลาเราดูว่าโค้ดไหนดีหรือไม่ดีในท้ายที่สุด

Credit

source: //msdn.microsoft.com/en-us/magazine/ff696759.aspx

ฝากข้อคิดเห็น

This site uses Akismet to reduce spam. Learn how your comment data is processed.