[jQuery] For Loops vs Each Loops while optimizing efficiency

[jQuery] For Loops vs Each Loops while optimizing efficiency

I have a trac plugin named DynamicFieldsPlugin. My studies show that this piece of code takes the most time. I know there are a lot of each used here. So I need your advice to improve its performance.

Here is my layout.js code:
  1. /*
  2.  * Layout 'class'
  3.  */
  4. var Layout = function(name){
  5.     this.name = name;
  6.     
  7.     // Selector for all field tds/ths
  8.     this.selector = '';

  9.     // Return the given field name's td/th element
  10.     this.get_tx = function(field){}
  11.     
  12.     // Return the given td/th element's field name 
  13.     this.get_field = function(tx){}
  14.     
  15.     // Move a field's tds and ths to slot i
  16.     this.move_field = function(field, i){}
  17.     
  18.     // Update the field layout given a spec
  19.     this.update = function(spec){
  20.         var this_ = this;
  21.         
  22.         // save original field order
  23.         if (window.dynfields_orig_field_order == undefined)
  24.             window.dynfields_orig_field_order = Object();
  25.         
  26.         if (window.dynfields_orig_field_order[this.name] == undefined){
  27.             window.dynfields_orig_field_order[this.name] = [];
  28.             jQuery(this.selector).each(function(i,e){
  29.                 var field = this_.get_field($(this));
  30.                 if (field)
  31.                     window.dynfields_orig_field_order[this_.name].push(field);
  32.             });
  33.         }
  34.         
  35.         // get visible and hidden fields
  36.         var visible = [];
  37.         var hidden = [];
  38.         jQuery.each(window.dynfields_orig_field_order[this.name],
  39.                     function(i,field){
  40.             var tx = this_.get_tx(field);
  41.             if (tx.hasClass('dynfields-hide')){
  42.                 hidden.push(field);
  43.             } else {
  44.                 visible.push(field);
  45.             }
  46.         });
  47.         
  48.         // get new field order
  49.         // warning: side-effects!
  50.         var new_fields = jQuery.merge(visible, hidden);
  51.         
  52.         // order the fields
  53.         this.order_fields(new_fields);
  54.     }
  55.     
  56.     this.order_fields = function(new_fields){
  57.         var this_ = this;
  58.         
  59.         // determine which fields need to move and move 'em!
  60.         jQuery(this.selector).each(function(i,e){
  61.             var field = this_.get_field($(this));
  62.             if (field == ''){
  63.                 var j = -1;
  64.             } else {
  65.                 var j = jQuery.inArray(field, new_fields);
  66.             }
  67.             
  68.             // check if field is in the correct slot in the new order
  69.             if (i != j && i < new_fields.length){
  70.                 // wrong order!
  71.                 this_.move_field(new_fields[i], i);
  72.             }
  73.         });
  74.     }
  75. };


  76. /*
  77.  * Inputs Layout implementation
  78.  */
  79. var inputs_layout = new Layout('inputs');

  80. // selector
  81. inputs_layout.selector = '#properties td[class!=fullrow]:parent';

  82. // get_tx
  83. inputs_layout.get_tx = function(field){
  84.     return jQuery('#field-'+field).parents('td:first');
  85. };

  86. // get_field
  87. inputs_layout.get_field = function(td){
  88.     var input = td.find(':input:first');
  89.     if (!input.length) return '';
  90.     return input.attr('id').slice(6);
  91. };

  92. // move_field
  93. inputs_layout.move_field = function(field, i){
  94.     var td = this.get_tx(field);
  95.     var th = td.parent('tr')
  96.                .find('th label[for=field-'+field+']')
  97.                .parent('th');
  98.     
  99.     // find correct row (tr) to insert field
  100.     var row = Math.round(i/2 - 0.5); // round down
  101.     row += jQuery('#properties td[class=fullrow]').length; // skip fullrows
  102.     var tr = jQuery('#properties tr:eq('+row+')');
  103.     
  104.     // find correct column (tx) to insert field
  105.     var col = 'col'+((i%2)+1);
  106.     if (tr.find('th').length){
  107.         if (col == 'col1'){
  108.             var old_th = tr.find('th:first');
  109.             if (old_th.get(0) != th.get(0)){ // don't move self to self
  110.                 old_th.before(th);
  111.                 old_th.before(td);
  112.             }
  113.         } else {
  114.             var old_td = tr.find('td:last');
  115.             if (old_td.get(0) != td.get(0)){ // don't move self to self
  116.                 old_td.after(td);
  117.                 old_td.after(th);
  118.             }
  119.         }
  120.     } else {
  121.         // no columns so just insert
  122.         tr.append(th);
  123.         tr.append(td);
  124.     }
  125.     
  126.     // let's set col
  127.     td.removeClass('col1 col2');
  128.     th.removeClass('col1 col2');
  129.     td.addClass(col);
  130.     th.addClass(col);
  131. };


  132. /*
  133.  * Header Layout implementation
  134.  */
  135. var header_layout = new Layout('header');

  136. // selector
  137. header_layout.selector = '#ticket .properties th:parent';

  138. // get_tx
  139. header_layout.get_tx = function(field){
  140.     return jQuery('#h_'+field);
  141. };

  142. // get_field
  143. header_layout.get_field = function(th){
  144.     return th.attr('id').slice(2);
  145. };

  146. // move_field
  147. header_layout.move_field = function(field, i){
  148.     var th = this.get_tx(field);
  149.     var td = th.parent('tr').find('td[headers=h_'+field+']');
  150.     
  151.     // find correct row (tr) to insert field
  152.     var row = Math.round(i/2 - 0.5); // round down
  153.     var tr = jQuery('#ticket .properties tr:eq('+row+')');
  154.     
  155.     // find correct column (tx) to insert field
  156.     if (tr.find('th').length){
  157.         if (i % 2 == 0){
  158.             var old_th = tr.find('th:first');
  159.             if (old_th.get(0) != th.get(0)){ // don't move self to self
  160.                 old_th.before(th);
  161.                 old_th.before(td);
  162.             }
  163.         } else {
  164.             var old_td = tr.find('td:last');
  165.             if (old_td.get(0) != td.get(0)){ // don't move self to self
  166.                 old_td.after(td);
  167.                 old_td.after(th);
  168.             }
  169.         }
  170.     } else {
  171.         // no columns so just insert
  172.         tr.append(th);
  173.         tr.append(td);
  174.     }
  175. };
I will happily give more information about this issue. Please help.