欢迎来到 jackNEss'窝窝
I like simple mind

浅谈 JS 性能优化

2011年09月08日

浅谈系统性能优化

作为一个网站开发web前端工程师,除了要确保页面上的各种功能和界面都完整的同时,性能优化也是一个不容忽视的工作,应该不断地,尽可能地对自己开发出来的网站项目的性能进行不断地改进及优化。

下面综合地来讲一下如何优化我们的 JS

来自 W3C Group 对于 JS 优化的几点细节:

一、避免出现脚本失控

不论什么脚本,在任何时间、任何浏览器上执行,都不应该超过100毫秒。如果实际执行的时间长于这个底限,一定要将进程分解成若干更小的代码段。

脚本失控基本上有以下四个方面的原因:

  1. 在循环中执行了太多的操作

    解决这个问题的诀窍就是用下面这两个问题来评估每个循环:

    • 这个循环必须要同步执行么?
    • 循环里面的数据,必须要按顺序执行么?

    如果1和2都可以否定,那么建议使用setTimeout方式将循环体切分成小块进行异步处理

    将循环中的定义变量及初始化操作放到循环外。参见:http://www.w3cgroup.com/article.asp?id=111

  2. 臃肿的函数体

    在JavaScript中,我们应该尽可能的用局部变量来代替全局变量!

    理解JavaScript作用域链。参见:http://www.jslab.org.cn/?tag=ScopeChainAndClosure

  3. 过多的递归

    使用迭代方式替代递归,采用memoization技术优化递归

    斐波那契数列的递归算法优化,参见:http://www.jslab.org.cn/?tag=Memoization

  4. 过多的DOM调用

    在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作。可你知道么?对DOM的操作是非常昂贵的,因为这会导致浏览器执行回流 (reflow)操作。而执行了过多的回流操作,你就会发现自己的网站变得越来越慢了。我们应该尽可能的减少DOM操作。

    回流操作主要会发生在几种情况下:
    • 改变窗体大小
    • 更改字体
    • 添加移除stylesheet块
    • 内容改变哪怕是输入框输入文字
    • CSS伪类被触发如 :hover
    • 更改元素的className
    • 当对DOM节点执行新增或者删除操作或内容更改时。
    • 动态设置一个style样式时(比如element.style.width=”10px”)
    • 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值(在兼容DOM的浏览器 中,可以通过getComputedStyle函数获取;在IE中,可以通过currentStyle属性获取)
    解决问题的关键,就是限制通过DOM操作所引发回流的次数:
    • 在对当前DOM进行操作之前,尽可能多的做一些准备工作,保证N次创建,1次写入
    • 在对DOM操作之前,把要操作的元素,先从当前DOM结构中删除:

      • 通过removeChild()或者replaceChild()实现真正意义上的删除
      • 设置该元素的display样式为“none”

      修改操作完成后,将上面这个过程反转过来,建议使用第2种方式

    • CSS部分

      另外一个经常引起回流操作的情况是通过style属性对元素的外观进行修改,如element.style.backgroundColor = “blue”;
      每次修改元素的style属性,都肯定会触发回流操作,要解决这个问题可以:

      • 使用更改className的方式替换style.xxx=xxx的方式
      • 使用style.cssText = ”;一次写入样式
      • 避免设置过多的行内样式
      • 添加的结构外元素尽量设置它们的位置为fixed或absolute
      • 避免使用表格来布局
      • 避免在CSS中使用JavaScript expressions(IE only)
    • 将获取的DOM数据缓存起来。这种方法,对获取那些会触发回流操作的属性(比如offsetWidth等)尤为重要
    • 当对HTMLCollection对象进行操作时,应该将访问的次数尽可能的降至最低,最简单的,你可以将length属性缓存在一个本地变量中,这样就能大幅度的提高循环的效率

二、避免大字符串字面量对象操作,如 字符串.lenth,尽量转换为new String(字符串)后再进行操作

三、在做字符查找替换等操作时善用正则表达式

快速掌握ECMAScript正则表达式。参见:http://www.w3cgroup.com/article.asp?id=202

四、减少语句,利用运算符优先级实现if else表达式,使用三元表达式,使用连续表达式(看情况,将损失程序可读性)

利用运算符优先级实现if else表达式参见:http://www.w3cgroup.com/article.asp?id=131

五、将CSS,JS文件合并到一个文件(非BT爱好者还是不要玩了^_^)

参见:http://www.w3cgroup.com/article.asp?id=29

六、避免Javascript事件绑定出现内存溢出

参见:http://www.w3cgroup.com/article.asp?id=207

七、使用WEB Workers技术(支持html5的浏览器)

Web Workers为JavaScript提供了一种能在后台进程中运行的方法,Web Workers进程能够在不影响用户界面的情况下处理任务。

参见:http://www.w3cgroup.com/article.asp?id=242

Y!14条(14 Rules for Faster-Loading Web Sites)

  1. Make Fewer HTTP Requests
  2. Use a Content Delivery Network (Server端)
  3. Add an Expires Header (Server端)
  4. Put Stylesheets at the Top
  5. Put Scripts at the Bottom
  6. Avoid CSS Expressions
  7. Make JavaScript and CSS External
  8. Reduce DNS Lookups (Server端)
  9. Minify JavaScript
  10. Avoid Redirects (Server端)
  11. Remove Duplicate Scripts
  12. Configure ETags (Server端)
  13. Make AJAX Cacheable
  14. Use Iframes Wisely

参见:http://www.w3cgroup.com/article.asp?id=97

九、微软早期的DHTML优化建议

  • 使用数组push替代字符串累加

个人对于算法上的一些建议:

  1. 关于JS的循环,循环是一种常用的流程控制。JS提供了三种循环:for(;;)、while()、for(in)。在这三种循环中 for(in)的效率最差,因为它需要查询Hash键,因此应尽量少用for(in)循环,for(;;)、while()循环的性能基本持平。当然,推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++或--运算符。
  2. 如果需要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免多次查询数组长度。
  3. 局部变量的访问速度要比全局变量的访问速度更快,因为全局变量其实是window对象的成员,而局部变量是放在函数的栈里的。
  4. 尽量少使用eval,每次使用eval需要消耗大量时间,这时候使用JS所支持的闭包可以实现函数模板。
  5. 尽量避免对象的嵌套查询,对于obj1.obj2.obj3.obj4这个语句,需要进行至少3次查询操作,先检查obj1中是否包含 obj2,再检查obj2中是否包含obj3,然后检查obj3中是否包含obj4…这不是一个好策略。应该尽量利用局部变量,将obj4以局部变量保存,从而避免嵌套查询。
  6. 使运算符时,尽量使用+=,-=、*=、\=等运算符号,而不是直接进行赋值运算。
  7. 当需要将数字转换成字符时,采用如下方式:”” + 1。从性能上来看,将数字转换成字符时,有如下公式:(“” +) > String() > .toString() > new String()。String()属于内部函数,所以速度很快。而.toString()要查询原型中的函数,所以速度逊色一些,new String()需要重新创建一个字符串对象,速度最慢。
  8. 当需要将浮点数转换成整型时,应该使用Math.floor()或者Math.round()。而不是使用parseInt(),该方法用于将字符串转换成数字。而且Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用时间,速度是最快的。
  9. 尽量作用JSON格式来创建对象,而不是var obj=new Object()方法。因为前者是直接复制,而后者需要调用构造器,因而前者的性能更好。
  10. 当需要使用数组时,也尽量使用JSON格式的语法,即直接使用如下语法定义数组:[parrm,param,param...],而不是采用 new Array(parrm,param,param…)这种语法。因为使用JSON格式的语法是引擎直接解释的。而后者则需要调用Array的构造器。
  11. 对字符串进行循环操作,例如替换、查找,就使用正则表达式。因为JS的循环速度比较慢,而正则表达式的操作是用C写成的API,性能比较好。

最后有一个基本原则,对于大的JS对象,因为创建时时间和空间的开销都比较大,因此应该尽量考虑采用缓存。

分类javascript
阅读 3,528
  • 评论加载中...

标签云

分类目录

最新留言

  • 评论加载中...

与我联系

如有疑问or建议可通过以下方式跟我取得联系.

Q Q:373435871
Email:jackness1208@gmail.com
© Copyright 2011 - 2014 jackNEss.org All Rights Reserved 粤ICP备14065612号
首页 | 关于我 | 网站地图 | RSS