优化JavaScript代码(Optimizing JavaScript code)

客户端脚本让你的应用程序更动态,但是浏览器解释脚本会带来低效率,不同客户端的性能也是不同的.下面我们讨论一些技巧和最好的实践来优化你的JavaScript代码
    使用字符串(working with string)
    String连接会给IE6和IE7垃圾回收造成影响.尽管这些问题已经得到在IE8解决--字符串连接的效率在IE8和其它非IE浏览器(如chrome)上有稍微提高.如果你的一大部分用户群体在使用IE6,7,你应该备加注意String的构造方式.
    看一下如下例子:

var  veryLongMessage  =
    'This is a 
long  string that due to our strict line length limit of'  +
    maxCharsPerLine 
+
    ' characters per line must be wrapped. ' 
+
    percentWhoDislike 
+
    '
%  of engineers dislike  this  rule. The line length limit is  for  '  +
    ' style purposes, but we don't want it to have a performance impact.' 
+
    ' So the question is how should we 
do  the wrapping ? '; 

 
    尝试用join来代替连接:

 1 var  veryLongMessage  =
 2     ['This is a  long  string that due to our strict line length limit of',
 3     maxCharsPerLine,
 4     ' characters per line must be wrapped. ',
 5     percentWhoDislike,
 6     ' %  of engineers dislike  this  rule. The line length limit is  for  ',
 7     ' style purposes, but we don't want it to have a performance impact.',
 8     ' So the question is how should we  do  the wrapping ? '
 9     ].join(); 
10

    
    同样的,在循环和/或条件语句中通过连接来构造字符串也是非常低效的.下面是一个错误的例子:

   var  fibonacciStr  =  'First  20  Fibonacci Numbers';
    
for  ( var  i  =   0 ; i  <   20 ; i ++ {
        fibonacciStr 
+=  i  +  '  =  '  +  fibonacci(i)  +  '';
    }
 


    正确的方式如下:

  var  strBuilder  =  ['First  20  fibonacci numbers:'];
    
for  ( var  i  =   0 ; i  <   20 ; i ++ {
      strBuilder.push(i, ' 
=  ', fibonacci(i));
    }

  
var  fibonacciStr  =  strBuilder.join(''); 

 

    通过助手函数生成字符串(Building strings with portions coming from helper functions)
    通过把字符串生成器传递到函数中来构造一个长字符串,要避免临时的String结果.
    例如,假设函数buildMenuItemHtml_ 需要用文字串(literal)和变量来构造String,并在内部使用了String构造器.而不是如下方式使用:

      var  strBuilder  =  [];
   
for  ( var  i  =   0 , length  =  menuItems.length; i  <  length; i ++ {
    strBuilder.push(
this .buildMenuItemHtml_(menuItems[i]));
  }

  
var  menuHtml  =  strBuilder.join(); 


推荐如下方式:

 

  var  strBuilder  =  [];
 
for  ( var  i  =   0 , length  =  menuItems.length; i  <  length; i ++ {
   
this .buildMenuItem_(menuItems[i], strBuilder);
 }

 
var  menuHtml  =  strBuilder.join(); 


  定义类方法(Defining class methods)
  下面的代码是低效率的,每生成一次baz.Bar对象,就会为其生成一个方法和闭包.

  baz.Bar  =   function ()  {
  
//  构造函数, constructor body
   this .foo  =   function ()  {
  
//  方法,method body
  }
;
 }


 推荐方式是:

 baz.Bar  =   function ()  {
  
//  构造函数,constructor body
 }
;

 baz.Bar.prototype.foo 
=   function ()  {
  
// 方法, method body
 }
;


 这种方式,不管有多少个baz.Bar对象生成,仅仅只建立一个方法foo且没有产生闭包.
 初始化实例变量(Initializing instance variables)
 在 原型(prototype)上用值类型[value type](而不是引用类型[reference type])声明或初始化实例变量.这避免了在每次调用构造函数时运行不必要的初始化代码.(有些情况下是不能这样做的:实例变量的初始值依赖于构造参数 或是在构造时的其它状态的变量(some other state at time of construction))
 例子:

 foo.Bar  =   function ()  {
  
this .prop1_  =   4 ;
  
this .prop2_  =   true ;
  
this .prop3_  =  [];
  
this .prop4_  =  'blah';
}


推荐的写法:

 闭包是JavaScript的一个强大且有用的特色;但是它有几个缺点:
 1,它们是常见的内存泄漏源
 2,生成闭包明显的比生成不是闭包的内部函数慢,比调用静态函数更慢.例如

foo.Bar  =   function ()  {
  
this .prop3_  =  [];
}
;
foo.Bar.prototype.prop1_ 
=   4 ;
foo.Bar.prototype.prop2_ 
=   true ;
foo.Bar.prototype.prop4_ 
=  'blah';    


 避开闭包的陷阱(Avoiding pitfalls with closures)

  function  setupAlertTimeout()  {
  
var  msg  =  'Message to alert';
  window.setTimeout(
function ()  { alert(msg); } 100 );
 }


 上面比下面的写法要慢:(上面的代码产生了闭包)

  function  setupAlertTimeout()  {
  window.setTimeout(
function ()  {
    
var  msg  =  'Message to alert';
    alert(msg);
  }
100 );
 }
 


 上面比下面的写法要慢:

  function  alertMsg()  {
  
var  msg  =  'Message to alert';
  alert(msg);
}


function  setupAlertTimeout()  {
  window.setTimeout(alertMsg, 
100 );
}
 


3,它们增加了变量的作用域(scope chain)的层次.当浏览器解析属性的时候,将会检查所有层次的作用域.见下面的例子:

var  a  =  'a';
function  createFunctionWithClosure()  {
  
var  b  =  'b';
  
return   function  ()  {
    
var  c  =  'c';
    a;
    b;
    c;
  }
;
}


var  f  =  createFunctionWithClosure();
f(); 


当执行f方法的时候,引用a比引用b慢,引用b比引用c慢.
IE中闭包更多信息可以查看 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies
避免用with(Avoiding with)
 在你代码中避免用with.它对性能有负面影响,因为它修改了作用域,在其它作用域查找变量的开销很大.
避免浏览器的内存泄露(Avoiding browser memory leaks)
内 存泄露是web应用程序普遍存在的问题,它会产生巨大的性能危害(hit).随着浏览器内存使用增加,你的web应用程序,用户系统其它部分操作,将变的 慢起来.大部分web应用程序的内存泄露是因为在 JavaScript脚本和Dom之间生成了循环引用(例如:javascript脚本和IE com结构之间,javascript脚本和Firefox xpcom结构之间)
 下面是避免内存泄漏一些经验法则:
 使用事件系统关联事件处理函数(Use an event system for attaching event handlers)
 大 部分的循环引用模式[DOM 元素-->事件处理函数(event handler)-->闭包(closure scope)-->DOM元素].为了避免这个问题,可以用经过充分测试事件系统(event system)来关联事件处理函数(event handlers),例如:Google doctype ,Dojo ,JQuery
 另外,在IE中使用用内联事件函数(inline event handlers)会导致另一种的类型的泄露.这不是通常的循环引用类型的泄露,而是由内部临时匿名脚本对象产生的泄露.详细信息,请看文章"DOM insertin Order Leak Model "和例子JavaScript Kit tutorial .
 避免扩展(expando)属性
 扩 展(expando)属性是把任意JavaScript的属性附加的到DOM元素上,这是循环引用产生的根源.你可以不产生内存泄露来扩展 (expando)属性,但是这是很容易产生内存泄露的.泄露模式是[DOM元素-->扩展(via expando)-->中间对象(intermediary object)-->DOM元素].最好是避免使用用它们.如果你用到它们,仅可使用原始类型(primitive types)作为值.如果不是原始类型,当扩展(expando)属性不在使用的时候,要把它置为空.可以参考文章"Circular References "

你可能感兴趣的:(JavaScript,浏览器,脚本,IE,prototype)