zoukankan      html  css  js  c++  java
  • Javascript程序优化

    继续我们的 Javascript 优化计划,上期已经做到怎么尽可能的缩小 Javascript 脚本的文件体积便于传输。不过这样做仅仅是不够的,因为 Javascript 代码的速度被分割成两部分:下载时间(取决于文件的大小)和执行速度(取决于代码算法)。

    当客户端载入 Javascript 脚本以后,真正的之行速度就取决于代码本身是否最优化了。这篇就是讲述如何优化代码本身的执行速度(听起来非常有技术的样子)。

    关注作用域
    浏览器中,Javascript 默认的变量范围是 window,也就是全局变量。在 window 中的变量只在页面从浏览器关闭以后才释放。而 Javascript 同时也有局部变量(私有变量)的概念,通常它在容器(比如 function)中执行完毕就会被释放。

    所以很容易理解当调用某变量时,解释器就会自下(容器)由上(window)寻找变量,寻找的变量本身也是需要一点时间的。所以,解释器在作用树(《Javascript 高级程序设计》中称为“范围树”)中遍历的范围越短,那么脚本运行就会越快。

    本人不擅长施教,下面的代码请自行理解

    var country = "China";

    function fn1() {
        alert(country);
    }

    function fn2() {
        var province = "Zhejiang";
        fn1();
    }

    function fn3() {
        var city = "Hangzhou";
        fn2();
    }

    fn3();扩展阅读请点击这里和这里。

    使用局部变量
    理解了上述的细节以后,接下来就非常可以理解了。使用局部变量可以带来更快的执行速度,因为解释器无需因为搜索变量而离开当前执行范围。同时,局部变量让允许完毕就会被释放,所以它们不会一直占用内存。

    这里要注意的是,使用闭包会打破这一规则,详细信息可以参看这里和以前我做的一道题目。

    避免使用 with 语句
    搜索变量范围越小,运行速度越快,所以就很很容易理解避免使用 with 语句的原因。比如

    alert(document.title);
    alert(document.body.tagName);
    alert(document.location);可以写成

    with (document) {
        alert(title);
        alert(body.tagName);
        alert(location);
    }虽然代码缩减的程度,并且也非常的容易理解。但是使用 with 语句的同时,要强制解释器不仅在作用树(范围树)内查找局部变量,还强制检测每个变量及指定的对象,看其是否有此变量或者属性。

    因此,最好避免使用 with 语句。最短的代码并不一定总是最高效的。

    选择正确的算法
    这似乎就是废话,所有的程序员都明白正确的算法对于之行效率是多么的重要。这里就不多解释,可以参考这篇和这篇。我始终相信,好的经验都是在实际 coding 中获得的。

    循环的花招
    Javascript 和大部分的程序语言一样,循环都会花费大量的执行时间,所以保持循环的高效可以减少执行时间。下面有几个花招,也是从那本书中获得的,照本宣科一下。

    反转循环
    有一个很有趣的例子,比如一个典型的循环会是这样写

    for (var i = 0; i < element.length; i++) {
        // ...
    }但写成下面这个样子就有助于降低算法的复杂度,因为它用常数(O)作为条件循环以减少执行时间

    for (var i = element.length - 1; i >= 0; i--) {
        // ...
    }书中的解释可能无法理解,那么我重新将其写成

    var element_length = element.length;
    for (var i = 0; i < element_length; i++) {
        // ...
    }可能会更好理解一些,因为它不会重复在循环中获取 element 的 length 属性,但书中的更改方法少了一个变量。

    翻转循环
    用 do...while 来替代 while 语句可以进一步的减少执行时间。比如

    var i = 0;
    while (i < element.length) {
       // ...
       i++;
    }可以改写为 do...while 语句为这个样子

    var i = 0;
    do {
        // ...
        i++;
    } while (i < element.length);当然,按照上一条的花招我们还可以优化成这个样子

    var i = element.length - 1;
    do {
        // ...
    } while (--i >= 0);这是因为 do...while 语句事先将循环体载入以后再做条件判断。不过本人认为还是保持程序的逻辑优先。

    条件判断
    优化 if 语句
    用 if 和多个 else 语句时,将就有可能的情况放在最先,依次类推。同时尽量减少 else 和 if 的数量,将条件按照二叉树的方式进行排列。例如

    if (i > 0 && i < 10) {
        alert('between 0 and 10');
    } else if (i > 9 && i < 20) {
        alert('between 10 and 20');
    } else if (i > 19 && i < 30) {
        alert('between 19 and 30');
    } else {
        alert('out of range');
    }可以将这段代码写成

    if (i > 0) {
        if (i < 10) {
            alert('between 0 and 10');
        } else {
            if (i < 20) {
                alert('between 10 and 20');
            } else {
                if (i < 30) {
                    alert('between 20 and 30');
                } else {
                    alert('Greater than or equal 30');
                }
            }
        }
    } else {
        alert('less than or equal 0');
    }这个样子。虽然看上去非常的复杂,但是它已经考虑了很多代码潜在的条件判断情况,所以执行得更快。

    switch 和 if
    用 switch 还是 if 已经是老生常谈的问题了。一般来说,超过两个 if...else 判断的时候,最好是使用 switch 语句。这样做可以使代码更加清晰并且效率更高。同时,case 条件也可以使用任何类型的值。

    语句瘦身
    其实非常可以容易理解,脚本中的语句越少,执行所需的时间越短(听起来与上述观点有矛盾)。有很多方法可以将代码中的语句缩短,比如下面的一些花招。

    定义多个变量
    很明显,一条语句可以定义多个变量。这样做不仅可以缩小代码体积,还可以减少语句数量以减少执行时间。比如下面的代码

    var webSite   = "www.gracecode.com";
    var haveLunch = function () {...}; 就可以精简为

    var webSite = "www.gracecode.com", haveLunch = function () {...};相信这样的语句也不会给阅读带来多大的障碍。

    迭代因子
    使用迭代因子,尽可能的合并语句。比如

    var girlFriend = girl[i];
    i++;这样的语句可以使用

    var girlFriend = girl[i++];替代。不过建议特别小心 i++ 和 ++i 的区别(该死的 C 语言后遗症)。

    使用数组和对象字面量
    这点其实在上一篇的时候就提到过,在这里就不复述。比如

    var mySite = new Object;
    mySite.author = "feelinglucky";
    mySite.location = "http://www.gracecode.com";就可以精简到

    var mySite = {author:"feeinglucky", location:"http://www.gracecode.com"};这样子。

    其他的花招
    优先使用内置方法
    比如

    function power(number, n) {
       var result = number;
       
       for (var i = 1; i < n; i++) {
           result *= number;
       }

       return result;
    }这样的函数,完全就可以使用 Math.pow 来完成。Javascript 已经有很多现成的内置方法,只要允许最好使用它们。

    存储常用的值
    当多次用到同一个值的时候,可以先将其存储在局部变量中,以便快速访问。这个就不复述了,偷个懒不好意思。

    DOM 操作
    节约 DOM 操作
    Javascript 对 DOM 的处理,可能是最耗费时间的操作之一。每次 Javascript 对 DOM 的操作,浏览器都会改变页面的表现、重新渲染页面,从而有明显的时间损耗。比较环保的做法就是尽可能不在 DOM 中进行 DOM 操作。

    请看下面的例子,为 ul 添加 10 个条目

    var oUl = document.getElementById("ulItem");

    for (var i = 0; i < 10; i++) {
        var oLi = document.createElement("li");
        oUl.appendChild(oLi);
        oLi.appendChild(document.createTextNode("Item " + i);
    }乍看起来似乎无懈可击,但是这段代码的确有问题。首先是循环中的 oUl.appendChild(oLi); 的调用,每次执行过后浏览器就会重新渲染页面;其次,给列表添加添加文本节点(oLi.appendChind(document.createTextNode("Item " + i);),也这会造成页面重新渲染。每次运行都会造成两次页面重新渲染,总计 20 次。

    要解决这个问题就如上面所言的,减少 DOM 操作,将列表项目在添加好文本节点以后再添加。下面的代码就可以与上述的代码完成同样的任务。

    var oUl   = document.getElementById("ulItem");
    var oTemp = document.createDocumentFragment();

    for (var i = 0; i < 10; i++) {
        var oLi = document.createElement("li");
        oLi.appendChild(document.createTextNode("Item " + i);
        oTemp.appendChild(oLi);
    }

    oUl.appendChild(oTemp);遵循标准的 DOM
    说点书中没有的(照本宣科完毕),Javascript 其实在寻找节点(Node)也会花上一段时间。对 Web 标准友好的 (x)html 文档相对杂乱文章的页面来说,Javascript 执行速度两者也会有所差别。

    浏览器处理页面有模式之分,这也许也是为什么要编写遵循 Web 标准的页面的原因之一。具体信息可以参考这里和一些言论。

    缓存 Ajax
    Ajax 虽然提供了页面异步请求调用,但别忘记了它还是访问服务器的。Javascript 作为驱动层本身可以作为缓存使用,虽然在页面重新载入后就会被释放,但对于服务器而言这是一个好的消息。

    结束语
    不知不觉就写了那么多,很多东西都是书上照本宣科的。《Javascript 高级程序设计》的确是一本不可多得的好书,建议大家有机会都可以去看看。这本书不贵,59 RMB(可能在别的地方还有打折),对于烟民而言也就一条双精度红喜,不过它可比香烟所能带来的快感多得多。
  • 相关阅读:
    第二次结对编程作业
    第5组 团队展示
    第一次结对编程作业
    第一次个人编程作业
    51 Nod 1024 Set
    51 Nod 1007 dp
    YY的GCD 数学
    选课 树形背包dp
    运输问题 费用流
    分配问题 费用流
  • 原文地址:https://www.cnblogs.com/top5/p/1862769.html
Copyright © 2011-2022 走看看