boiled water

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
我google一下,已有人翻译了此文.感觉比我翻译的要好!是译言站翻译的
www.yeeyan.com/articles/view/92135/47626/dz
原文见:http://code.google.com/intl/zh-CN/speed/articles/optimizing-javascript.html

不合适的地方,请大家指出来!希望对你有用!

                                                            优化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来代替连接:
    

 1var 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';
}


推荐的写法:

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


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

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

 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"

posted on 2009-12-14 22:33  boiled water  阅读(2453)  评论(1编辑  收藏  举报