• javascript代码优化


    注释

            代码中的注释很重要,自然也是毋庸置疑的。通常我们会强调代码中注释数量的多少,而轻视了对注释质量的提高。编码是及时添加注释,会给后续代码的维护人员带来很大的便利。但是如果注释不注意更新,或者由于拷贝、粘贴引起的错误的注释,则会误导阅读人员,反而给阅读带来障碍。
            除了注释要及时更新外,我们还应对注释的内容要特别关注。注释要尽量简单、清晰明了,避免使用含混晦涩的语言,同时着重 注释的意义,对不太直观的部分进行注解。JavaScript 的注释有两种"//" 和"/* .... */"。
            建议 // 用作代码行注释,

      /* .... */ 形式用作对整个代码段的注销,或较正式的声明中,如函数参数、功能、文件功能等的描述中。

    命名规则

    JavaScript 中的标识符的命名规则:

            1、以字母、下划线'_'或美元符号'$'开头

            2、允许名称中包含字母,数字,下划线'_'和美元符号'$'

            3、区分大小写 变量、参数、成员变量、函数等名称均以小写字母开头,构造器的名称以大写字母开头。下划线'_'开头的变量一般习惯于标识私有 / 局部成员。而美元符号'$'开头的变量习惯于标识系统相关。

    建议:

            $开头变量表示jquery-dom节点。

            全大写表常量(js无常量,用这种方式区分)。

            普通变量用骆峰命名(treeName)。

            全局变量或闭包变量用骆峰命名(TreeName)。

            函数尽量用动名词组合(如setName)。

            变量尽量用名词。

    变量的声明    

            尽管 JavaScript 语言并不要求在变量使用前先对变量进行声明。但我们还是应该养成这个好习惯。这样可以比较容易的检测出那些未经声明的变量,避免其变为隐藏的全局变量,造成隐患。 在函数的开始应先用 var 关键字声明函数中要使用的局部变量,注释变量的功能及代表的含义。每个变量单独占一行,以便添加注释。这是因为 JavaScript 中只有函数的 {} 表明作用域,用 var 关键字声明的局部 变量只在函数内有效,而未经 var 声明的变量则被视为全局变量。

    减少页面重绘

            减少页面重绘虽然本质不是JS本身的优化,但是其往往是由JS引起的,而重绘的情况往往是严重影响页面性能的,所以完全有必要拿出来,我们看下面例子:

     <div id="demo"></div>   
     <input type="button" value="效率低" onclick="func1()" />   
     <input type="button" value="效率高" onclick="func2()" /> 
     var str = "<div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div>";   
     //效率低的   
     function func1(){   
         var obj = document.getElementById("demo");   
         var start = new Date().getTime();   
         for(var i = 0; i < 100; i++){   
             obj.innerHTML += str + i;   
         }   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     }   
     //效率高的   
     function func2(){   
         var obj = document.getElementById("demo");   
         var start = new Date().getTime();   
         var arr = [];   
         for(var i = 0; i < 100; i++){   
             arr[i] = str + i;   
         }   
         obj.innerHTML = arr.join("");   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     } 

            在例子中,我只是用了100次的循环,因为如果用10000次循环的话,浏览器基本上就卡住不动了,但是即使是100次的循环,我们看看下面的执行结果。

      

            可以看到的是,这简直是一个惊人的结果,仅仅100次的循环,出现了如此之大的差别。这里还要注意的是,一般影响页面重绘的不仅仅是innerHTML,如果改变元素的样式,位置等情况都会触发页面重绘,所以在平时一定要注意这点。

    for循环

            for循环是我们经常会遇到的情况,我们先看看下面例子:

     <input type="button" value="效率低" onclick="func1()" />   
     <input type="button" value="效率高" onclick="func2()" /> 
     var arr = [];   
     for(var i = 0; i < 10000; i++){   
         arr[i] = "<div>" + i + "</div>";   
     }   
     document.body.innerHTML += arr.join("");   
         
     //效率低的   
     function func1(){   
         var divs = document.getElementsByTagName("div");   
         var start = new Date().getTime();   
         for(var i = 0; i < divs.length; i++){   
             //"效率低"   
         }   
         var end = new Date().getTime();   
         alert("用时:" + (end - start) + "毫秒");   
     }   
     //效率高的   
     function func2(){   
         var divs = document.getElementsByTagName("div");   
         var start = new Date().getTime();   
         for(var i = 0, len = divs.length; i < len; i++){   
             //"效率高"   
         }   
         var end = new Date().getTime();   
         alert("用时:" + (end - start) + "毫秒");   
     } 

            这里for循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用for循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。

     <input type="button" value="效率低" onclick="func1()" />   
     <input type="button" value="效率高" onclick="func2()" />  
     var arr2 = [];   
     for(var i = 0; i < 10000; i++){   
         arr2[i] = "<div>" + i + "</div>";   
     }   
     //效率低的   
     function func1(){   
         var start = new Date().getTime();   
         for(var i = 0; i < arr2.length; i++){   
             //"效率低"   
         }   
         var end = new Date().getTime();   
         alert("用时:" + (end - start) + "毫秒");   
     }   
     //效率高的   
     function func2(){   
         var start = new Date().getTime();   
         for(var i = 0, len = arr2.length; i < len; i++){   
             //"效率高"   
         }   
         var end = new Date().getTime();   
         alert("用时:" + (end - start) + "毫秒");   
     } 

            对于for循环的优化,也有人提出很多点,有人认为用-=1,或者从大到小的方式循环等等,我认为是完全没有必要的,这些优化往往实际情况下根本没有表现出来,换句话说只是计算机级别的微小的变化,但是给我们带来的却是代码的可读性大大的降低,所以实在是得不偿失。

    减少作用域链上的查找次数

            我们知道,js代码在执行的时候,如果需要访问一个变量或者一个函数的时候,它需要遍历当前执行环境的作用域链,而遍历是从这个作用域链的前端一级一级的向后遍历,直到全局执行环境,所以这里往往会出现一个情况,那就是如果我们需要经常访问全局环境的变量对象的时候,我们每次都必须在当前作用域链上一级一级的遍历,这显然是比较低效一点的。

    避免双重解释

            双重解释的情况也是我们经常会碰到的,有的时候我们没怎么考虑到这种情况会影响到效率,双重解释一般在我们使用eval、new Function和setTimeout等情况下会遇到,我们看看下面的例子:

     <div id="demo"></div>   
     <input id="but1" type="button" onclick="func1()" value="效率低"/>   
     <input id="but2" type="button" onclick="func2()" value="效率高"/>   
     var sum, num1 = 1, num2 = 2;   
     function func1(){   
         var start = new Date().getTime();   
         for(var i = 0; i < 10000; i++){   
             var func = new Function("sum+=num1;num1+=num2;num2++;");   
             func();   
         }   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     }   
         
     function func2(){   
         var start = new Date().getTime();   
         for(var i = 0; i < 10000; i++){   
             sum+=num1;   
             num1+=num2;   
             num2++;   
         }   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     } 
     var sum, num1 = 1, num2 = 2;   
     function func1(){   
         var start = new Date().getTime();   
         for(var i = 0; i < 1000; i++){   
             eval("sum+=num1;num1+=num2;num2++;");   
         }   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     }   
     function func2(){   
         var start = new Date().getTime();   
         for(var i = 0; i < 1000; i++){   
             sum+=num1;   
             num1+=num2;   
             num2++;   
         }   
         var end = new Date().getTime();   
         alert("用时 " + (end - start) + " 毫秒");   
     } 

      双重解释是有一定开销的,所以在实际当中要尽量避免双重解释。

    使用一次innerHTML赋值代替构建dom元素

    对于大的DOM更改,使用innerHTML要比使用标准的DOM方法创建同样的DOM结构快得多。

    var frag = document.createDocumentFragment();
            for (var i = 0; i < 1000; i++) {
                var el = document.createElement('p');
                el.innerHTML = i;
                frag.appendChild(el);
            }
            document.body.appendChild(frag);
            //可以替换为:
            var html = [];
            for (var i = 0; i < 1000; i++) {
                html.push('<p>' + i + '</p>');
            }
            document.body.innerHTML = html.join('');

    条件分支

            将条件分支,按可能性顺序从高到低排列:可以减少解释器对条件的探测次数

            在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。

            使用三元运算符替代条件分支

     if (a > b) {
                num = a;
            } else {
                num = b;
            }
            //可以替换为:
            num = a > b ? a : b;

    循环引用

            如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

            简单的循环引用:

    var el = document.getElementById('MyElement');
            var func = function () {
                //…        }
            el.func = func;
            func.element = el;

    但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。

    function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
            }
            init();

            init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。

            下面2种方法可以解决循环引用:        

            1)  置空dom对象

    function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
            }
            init();
            //可以替换为:
            function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
                el = null;
            }
            init();

            将el置空,context中不包含对dom对象的引用,从而打断循环应用。

            如果我们需要将dom对象返回,可以用如下方法:

     function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
                return el;
            }
            init();
            //可以替换为:
            function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
                try {
                    return el;
                } finally {
                    el = null;
                }
            }
            init();

            2)  构造新的context

    function init() {
                var el = document.getElementById('MyElement');
                el.onclick = function () {
                    //……            }
            }
            init();
            //可以替换为:
            function elClickHandler() {
                //……        }
            function init() {
                var el = document.getElementById('MyElement');
                el.onclick = elClickHandler;
            }
            init();

            把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。

    释放dom元素占用的内存

            将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。

            在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。

    释放javascript对象

            在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。

            对象:obj = null

            对象属性:delete obj.myproperty

            数组item:使用数组的splice方法释放数组中不用的item

      函数节流(throttle)与函数去抖(debounce)

      函数去抖:

      如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。

      也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

    var debounce = function(idle, action){
      var last
      return function(){
        var ctx = this, args = arguments
        clearTimeout(last)
        last = setTimeout(function(){
            action.apply(ctx, args)
        }, idle)
      }
    }

      函数节流:

      如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。

      也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。

    var throttle = function(delay, action){
      var last = Date.now();
      return function(){
        var curr = Date.now();
        if (curr - last > delay){
          action.apply(this, arguments)
          last = curr 
        }
      }
    }
  • 相关阅读:
    Hive分桶bucket
    Error: java.io.IOException: org.apache.hadoop.hdfs.BlockMissingException: Could not obtain block
    接上一篇嵌套循环问题之解决
    RPA自动化机器人uibot之循环嵌套坑
    Chrom谷歌浏览器没网之最全解决办法之一
    (语法糖)列表生成式之怪诞--(暂时无法理解)
    浅析scrapy与scrapy-redis的区别
    浅谈Python中函数式编程、面向对象编程以及古怪的PythonIC
    OpenCV+TensorFlow图片手写数字识别(附源码)
    TensorFlow Object Detection API中的Faster R-CNN /SSD模型参数调整
  • 原文地址:https://www.cnblogs.com/web-easy/p/7767430.html
Copyright © 2020-2023  润新知