zoukankan      html  css  js  c++  java
  • 浅谈js性能优化

    最近刚阅读完《高性能javascript》,想谈谈对js性能优化的看法。理解有些不同,可能还需要各位多多提醒。

    话不多说,提到javascript难免会联想到文档对象模型(DOM),它作用于XML和HTML文档的程序接口(API),位于浏览器中,主要用来与HTML文档打交道。同样也用于Web程序中获取XML文档,并使用DOM API来访问文档中的数据。尽管DOM是个与语言无关的API,它在浏览器中的接口却是用javascript实现的。客户端脚本编程大多数时候是在和底层文档(underlying document) 打交道,DOM就成为现在javascript编程中的重要部分。

    浏览器通常会把DOM和js独立实现。比如在IE中,javascript的实现名为Jscript,位于jscript.dll文件中;DOM的实现则存在另一个库中,名为mshtml.dll(内部称为Trident)。这个分离允许的其他技术和语言,比如VBScript,能共享使用DOM以及Trident提供的渲染函数。Safari中的DOM和渲染使用的Webkit中的WebCode实现,javascript部分是由独立的javascriptCode引擎(最新版本的名字为SquirrelFish)来实现。Google Chrome同样使用WebKit中的WebCore库来渲染页面,但javascript引擎是他们自己研发的,名为V8。Firefox的javascript引擎名为SpiderMonkey(最新版的名字为TraceMonkey),与名为Gecko的渲染引擎相互独立。

    把DOM和javascript(这里指ECMAScript,JavaScript使用的ECMAScript版本为ECMAScript-262)各自想象一个岛屿,它们之间用收费桥梁连接。ECMAScript每次访问DOM,都需要途经这座桥,并交纳“过桥费”。访问DOM的次数越多,费用越高。所以想办法减少过桥次数就可以减少费用。

    一、超载运输

    上面提到“过桥费”很贵啊,那么我们尽量使需要多次去访问DOM的时候全部整合到一次。比如最简单的例子:

    function innerHTMLLoop(){
         for(var count = 0;count < 15000 ;count++){
               document.getElementById('here').innerHTML +='a';
        }  
    }    

    这个函数循环修改页面元素的内容。这段代码存在循环迭代,该元素都被访问两次,一次是读取innerHTML属性值,另一次是重写它。(意味着每次循环都必须付“过桥费”)。

    为了减少费用,我们采取一种更高效的方法,例:

    function innerHTMLLoop2(){
         var content = ' ';
         for(var count = 0;count < 15000 ;count++){
               content +='a';
        }  
        document.getElementById('here').innerHTML +=content;
    }    

    这样只需要付一次“过桥费”,就可以完成相同的功能。运行速度在不同的浏览器中都有大幅度的提升,例如IE6中,使用innerHTMLLoop2()比使用innerHTMLLoop()快155倍。(所以现实当中好多大卡车超载也是为了省这个费用,一次性多赚点。不过还是量力而行。程序也是一样,均衡存乎万物之间。)

    二、触手可及

    尽管使用优化过的javascript引擎的新型浏览器,对于对象成员引用也存在一些性能问题。对象在原型链中存在的未知越深,找到它也就越慢,例如不太常见的写法:window.location.href。每次遇到点操作符,嵌套成员会导致Javascipt引擎搜索所有对象成员。对象成员嵌套得越深,读取速度就会越慢。执行location.href总是比window.location.href要快,后者也比window.location.href.toString()要快。如果这些属性不是对象的实例属性,那么成员解析还需要搜索原型链,这会花更多的时间。

    由于类似的性能问题都是与对象成员有关,因此应该尽可能避免使用它们。更准确地说,应当注意,只在必要时使用对象成员。例如,在同一个函数中没有必要多次读取同一个对象成员。例:

    function hasEitherClass(element,className1,className2){
          return element.className == className1 || element.className == className2;  
    }

    以上的代码,element.className读取了2次。意味着在该函数语句中2次成员查找都是通过读取属性值,那么有必要子啊第一次查找到值后就将其存储在局部变量中,因为局部变量的读取速度要快很多。例:

    function hasEitherClass(element,className1,className2){
          var currentClassName = element.className;
          return currentClassName  == className1 || currentClassName == className2;  
    }

    上面element.className 赋值在currentClassName局部变量,避免了多次查找带来的性能开销。(多次需要全局对象成员,那就赋值在最容易拿到的地方,这样可以减少去搜索和查找)

    总结

    虽然我还有很多要讲,但是太多太多的方式可以进行性能优化,以后有更好的再补充。不过优化就是跟人找方法用最小的力量去做最大的事情一样,说俗点就是“懒”,我们让程序也懒。

    下面我看一个讲DOM重绘(repaint)、重新排版(reflow)操作文章链接:http://www.cnblogs.com/hyddd/archive/2013/02/07/2908960.html

    还有一个提供一些优化技巧http://www.cnblogs.com/fullhouse/archive/2012/01/05/2312956.html

    反模式:http://wiki.jikexueyuan.com/project/javascript-design-patterns/anti-patterns.html

    希望对大家有所帮助!谢谢!

  • 相关阅读:
    游标定位:Cursor类
    拨号操作——android.intent.action.CALL
    按键——Button事件监听器
    文本框——EditText
    配置文件:mainfest.xml
    win7如何设置以管理员身份运行
    随机数:rand()
    std::string 和 CString问题
    文件操作:rewind()
    文件操作:fread()和fwrite()
  • 原文地址:https://www.cnblogs.com/zrl66/p/6364308.html
Copyright © 2011-2022 走看看