javascript代码优化

注释javascript

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

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

命名规则java

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

        一、以字母、下划线'_'或美圆符号'$'开头数组

        二、容许名称中包含字母,数字,下划线'_'和美圆符号'$'浏览器

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

建议:app

        $开头变量表示jquery-dom节点。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 
    }
  }
}
相关文章
相关标签/搜索