💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## 循环专题 > * 循环是一种常用的流程控制。 > > > * `JAVASCRIPT`提供了三种循环。 > > > * `for(;;)`。 > > > * 推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的`++`或`–-`运算符。 > * 代码的可读性对于for循环的优化。 > * 用`-=1`。 > * 从大到小的方式循环(这样缺点是降低代码的可读性)。 > > > > ~~~ > /**效率低**/ > var divs = document.getElementsByTagName("div"); > for(var i = 0; i < divs.length; i++){ > ... > } > /**效率高,适用于获取DOM集合,如果纯数组则两种情况区别不到**/ > var divs = document.getElementsByTagName("div"); > for(var i = 0, len = divs.length; i < len; i++){ > ... > } > /**在`IE6.0`下,`for(;;)`循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用`for(;;)`循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。**/ > ~~~ > > > * `while()`。 > * `for(;;)`、`while()`循环的性能基本持平。 > * `for(in)`。 > * 在这三种循环中`for(in)`内部实现是构造一个所有元素的列表,包括`array`继承的属性,然后再开始循环,并且需要查询hasOwnProperty。所以`for(in)`相对`for(;;)`循环性能要慢。 > * 选择正确的方法 > > > * 避免不必要的属性查找。 > > > * 访问`变量`或`数组`是`O(1)`操作。 > * 访问`对象`上的`属性`是一个`O(n)`操作。 > > 对象上的任何属性查找都要比访问变量或数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索,即属性查找越多,执行时间越长。所以针对需要多次用到对象属性,应将其存储在局部变量。 > > > * 优化循环。 > > > * 减值迭代。 > * 大多数循环使用一个从0开始,增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加有效。 > * 简化终止条件。 > * 由于每次循环过程都会计算终止条件,故必须保证它尽可能快,即避免属性查找或其它O(n)的操作。 > * 简化循环体。 > * 循环体是执行最多的,故要确保其被最大限度地优化。确保没有某些可以被很容易移出循环的密集计算。 > * 使用后测试循环。 > * 最常用的for和while循环都是前测试循环,而如do-while循环可以避免最初终止条件的计算,因些计算更快。 > for(var i = 0; i 优化1:简化终止条件 > for(var i = 0, len = values.length; i 优化2:使用后测试循环(注意:使用后测试循环需要确保要处理的值至少有一个) > var i values.length - 1; if(i > -1) { do { process(values[i]); }while(--i >= 0); } > * 展开循环。 > * 当循环的次数确定时,消除循环并使用多次函数调用往往更快。 > * 当循环的次数不确定时,可以使用Duff装置来优化。 > * Duff装置的基本概念是通过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。 > // Jeff Greenberg for JS implementation of Duff's Device // 假设:values.length > 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; }while(--iterations > 0); > 如上展开循环可以提升大数据集的处理速度。接下来给出更快的Duff装置技术,将do-while循环分成2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。) > // Speed Up Your Site(New Riders, 2003) function process(v) { alert(v); } > var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover > 0) { do { process(values[i++]); }while(--leftover > 0); } > do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0); > 针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。 > * 避免在循环中使用`try-catch`。 > > > * `try-catch-finally`语句在catch语句被执行的过程中会动态构造变量插入到当前域中,对性能有一定影响。 > > * 如果需要异常处理机制,可以将其放在循环外层使用。 > > > * 循环中使用try-catch > > > > ~~~ > for ( var i = 0; i < 200; i++) { > try {} catch (e) {} > } > ~~~ > > > * 循环外使用try-catch > > > > ~~~ > try { > for ( var i = 0; i < 200; i++) {} > } catch (e) {} > ~~~ > > > * 避免遍历大量元素: > > > * 避免对全局`DOM`元素进行遍历,如果`parent`已知可以指定`parent`在特定范围查询。 > > > > ~~~ > var elements = document.getElementsByTagName( '*' ); > for (i = 0; i < elements.length; i++) { > if (elements[i].hasAttribute( 'selected' )) {} > } > ~~~ > > > > 如果已知元素存在于一个较小的范围内, > > > > ~~~ > var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' ); > for (i = 0; i < elements.length; i++) { > if (elements[i].hasAttribute( 'selected' )) {} > } > ~~~