网站地图    收藏   

主页 > 前端 > javascript >

javascript循环用法与优化探究

来源:自学PHP网    时间:2014-09-19 14:47 作者: 阅读:

[导读] 本文章来给各位同学介绍一下在使用js事种循环时一些性能上的优化方法,下面我来介绍一些JS的基础知识,发现JS循环同其他语言的循环大有不同,随后再继续翻了俩本JS的权威教程,写...

平时我们书写循环大致是这个样子的:

 代码如下 复制代码
function(){
    //一般循环的书写方式
    for(var i=0; i<values.length; i++){
        ...
    }
}
 

优化变量声明

上面这个写法在一开始学习JS的时候是没有错的,甚至对于绝大多数的面向对象语言这么写都是正确的书写方式,但JS不同与其他面向对象的语言,他没有块及作用域,有的仅仅是函数作用域,所以说,上面的写法并不是很规范,在某些情况下还会发生Bug ,当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于JavaScript,只要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。合理的写法即把变量声明在函数的开始,而不是在循环内部才开始定义变量。

 代码如下 复制代码
function func(){
    //优化JS的变量定义,变量定义在开始位置,避免产生块级作用域的误区
    var i;
    for(i=0; i<values.length; i++){
        ...
    }
}

优化循环中动态集合读取

优化了JS 循环的变量后,我们继续深入优化。日常开发最常打交道的就是DOM 了,常常会遇到循环NodeList 的情况(不知道什么是NodeList 对象的话请去复习基础知识)。总而言之一般类似var divs = document.getElementsByTagName(“div”) 的这个divs 所引用的就是NodeList 对象,其他类似的NodeList 近亲有NameNodeMap 和 HTMLCollection, 这三个集合每当文档结构发生变化时,它们都会得到更新(而且还是动态的更新)。这样就会导致俩个问题,第一是性能问题,每当你修改NodeList 在读取它时,你读取的不是先前的NodeList 而是修改后动态更新的NodeList。第二会产生无限循环的Bug。举个例子,下列代码会导致无限循环:

 

 代码如下 复制代码
function func(){
    //导致NodeList无限循环的循环书写方式
    var divs = document.getElementsByTagName("div");
    for(var i=0; i<divs.length; i++){
        var div =document.createElement("div");
        document.body.appendChild(div);
        alert("Infinite loop");
    }
}
func()

上例所示的循环代码会导致一个严重的问题,每次循环都要对divs.length 求值,意味着会运行取得所有div元素的查询,之后创建一个新的div元素添加的文档中,因此div.length 的值每次循环后都会递增。既然i 和divs.length 每次都会同时递增,结果他们的值永远不会相等,从而导致了无限循环。

为了避免出现这样的低效率甚至于隐含Bug的循环书写方式,对策就是尽量减少访问NodeList 的次数。因为每次访问NodeList,都会运行一次基于文档的查询。所以可以考虑从NodeList 中取得的值缓存起来。下面是优化后的写法:

 代码如下 复制代码
function func(){
    //避免NodeList无限循环的循环书写方式
    var divs = document.getElementsByTagName("div");
    for(var i=0, len = divs.length ; i<len; i++){
        var div =document.createElement("div");
        document.body.appendChild(div);
        alert("Infinite loop will not happen");
    }
}
func()

很简单吧,只要把divs.length 缓存起来放在一个变量len 里面,那么无论NodeList以后怎么变,都避免读取,避免无限循环发生,因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,利用局部变量,把它放入一个临时的地方进行查询。如果每次查询div.length,就要额外进行一个操作,而预先把var len=div.length,则就少了一次查询,性能的优化大大体现。 开始你会觉得这样书写很不适应,但多写几遍,你很快就会习惯这样“别扭”书写方式的。

优化继续优化!

用i+=1 代替i++

这个是从《Javascript语言精粹》里面看来的,大致意思是说用了i++ 会有潜在的安全问题,++这个运算符可以前置又可以后置使得自由度过大,如果出现结尾没有分号会导致些许的错误例如:a++b++c,你很难弄明白前面那个代码是什么意思。根据《Javascript语言精粹》上面提到,这个++ 或者–– 运算符怂恿过于诡异的写法而促使出现糟糕的代码。除了错误的架构外,它们是导致病毒和其他安全威胁的第二大元凶。所以JsLint有个选项plusplus 是检测是否禁止使用了这些运算符。还有个原因是i+=1 比起i++ 更加“原生”可以提升性能,但是个人认为可读性降低,根据需要各自取舍吧。那么我们的代码又要变成这个样子:

 代码如下 复制代码
function func(){
    //减值迭代优化循环
    for(var i=values.length-1; i>=0; i--){
    ...
    }
}

让优化来的更猛烈些吧!

减值迭代优化循环

大多数循环使用一个从0开始、增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加高效。如果值的处理顺序无关紧要,那么循环可以改为i 减值,优化如下:

 代码如下 复制代码
function func(){
    //循环优化大串联
    var i;  //优化变量声明
    var divs = document.getElementsByTagName("div");
    for(i=divs.length-1; i>=0; i-=1){  //优化循环中动态集合读取、减值迭代、用i-=1 代替 i--
    ...
    }
}

循环优化大串联

如果我们把上面所有的循环优化方式集中在一块,写法会是这样子滴:

 代码如下 复制代码
12345678 function func(){     //循环优化大串联     var i;  //优化变量声明     var divs = document.getElementsByTagName("div");     for(i=divs.length-1; i>=0; i-=1){  //优化循环中动态集合读取、减值迭代、用i-=1 代替 i--     ...     } }

老实说,上面这个循环优化大串联性能是提高了,但可读性也降低到了一个新的高度。你代码写的再风骚,也得让人有个接受的地步那,做为团队开发,上面这个串联似乎还是不是特别可取,个人还是提倡部分优化,以优化性能的前提下兼顾可读性:

 代码如下 复制代码

function func(){
    //合理的循环优化
    var i,len;  //优化变量声明
    var divs = document.getElementsByTagName("div");
    for(i=0, len = divs.length; i<len; i++){  //优化循环中动态集合读取,必要时也可以用i+=1 代替 i++
    ...
    }
}

个人比较倾向于上面的这段JS循环。合理优化循环提升了代码的性能,又兼顾了可读性,可谓是一箭双雕,万事大吉

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论