性能(js)
- 1.避免全局查找:
1 <script type="text/javascript"> 2 function updateUI(){ 3 var imgs=document.getElementsByTagName("img"); 4 for(var i=0,len=imgs.length;i<len;i++){ 5 imgs[i].title=document.title+"image"+i; 6 } 7 var msg=document.getElementById("msg"); 8 msg.innerHTML="updated!" 9 } 10 updateUI(); 11 </script>
可能优化脚本性能最重要的就是:注意全局查找。使用全局变量和函数的开销比局部变量更大,因为涉及作用域链上的查找。以上例子包含了三个对全局document对象的引用。当图片特别多时,会造成对此作用域链上的查找。在这里,可以创建一个指向document对象的局部变量。来改进性能。如下:
1 <script type="text/javascript"> 2 function updateUI(){ 3 var doc=document; 4 var imgs=doc.getElementsByTagName("img"); 5 for(var i=0,len=imgs.length;i<len;i++){ 6 imgs[i].title=doc.title+"image"+i; 7 } 8 var msg=doc.getElementById("msg"); 9 msg.innerHTML="updated!" 10 } 11 updateUI(); 12 </script>
在这里,首先将document对象保存在本地的doc变量中,后边的引用均为该本地变量,现在函数只有一次全局查找,肯定更快。
- 2.避免with语句
和函数相同,with会创建自己的作用域。从而增加其中执行代码的作用域链长度。由于额外作用域的查找,在with语句中执行的代码肯定比外面执行的代码要慢。如下:
1 function updateBody(){ 2 with(document.body){ 3 alert(tagName); 4 innerHTML="hello world!"; 5 } 6 } 7 updateBody();
改进:虽然使用with语句可以创建document.body的作用域,在内部直接调用它的方法和属性即可。但可以使用局部变量来达到相同的效果。
1 function updateBody(){ 2 var body=document.body; 3 alert(body.tagName); 4 body.innerHTML="hello world!"; 5 } 6 updateBody();
以上均为:需要不断查询作用域链,从而造成开销。可通过创建局部变量的方法。
- 选择正确方法
- 对象属性查找:使用变量和数组要比访问对象上的属性更高效。对象上的属性查找花费时间比较长,因需要在原型链中对该属性进行一次搜索。所以:一旦多次用到对象属性,应该将其存储在局部变量中。
- 优化循环: (1).减值迭代 (2)简化终止条件 (3)简化循环体 (4)使用后测试循环
(1):大多数循环使用从0开始、增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更高效。
(2):由于每次循环都会计算终止条件,所以必须保证终止条件的简单,避免属性查找和其他操作。
(3):确保循环体的优化。
(4):使用后测试优化---最常使用for循环和while循环 都属于前测试循环。而do-while这种后测试循环,避免终止条件的计算,运算更快。
- 最小化语句数
- 多个变量声明:变量声明只用一个var语句,之后用逗号隔开。比单个变量分别声明要快很多。
- 插入迭代值 :当使用迭代值时(在不同位置进行增加和减少的值),尽可能合并语句。如:
1 var name=values[i]; 2 i++; 3 4 更正为: 5 var name=values[i++];
-
使用数组和对象字面量:
- 优化DOM交互
- 最小化现场更新:一旦访问的DOM部分是已经显示的页面的一部分,那么就要进行现场更新。即需要对页面对用户的显示进行更新。例如:
1 var list=document.getElementById("list"); 2 for(var i=0;i<10;i++){ 3 var item=document.createElement("li"); 4 list.appendChild(item); 5 item.appendChild(document.createTextNode("item"+i)); 6 }
因为每次添加项目时都要有两个现场更新,添加<li>元素和添加文本节点。
解决方法:第一种,将列表从页面移除,最后进行更新,最后将列表插入原位置。不理想:每次页面更新会有闪烁。
第二种,使用文档片段来构建DOM结构,接着将其添加到list元素。片段本身不会被添加,片段中的子节点会被添加到目标。
-
1 var list=document.getElementById("list"), 2 fragment=document.createDocumentFragment(), 3 item, 4 i; 5 for(i=0;i<10;i++){ 6 item=document.createElement("li"); 7 fragment.appendChild(item); 8 item.appendChild(document.createTextNode("item"+i)); 9 } 10 list.appendChild(fragment);
2.使用innerHTML。创建一个字符串然后一次性调用innerHTML要比调用innerHTML多次快的多。
3.使用事件代理。页面上的事件处理程序的数量和页面响应用户交互的速度之间有个负相关,为了减少这种惩罚,最好事件代理:利用事件冒泡,任何可以冒泡的事件都不仅仅可以可以在事件目标上进行处理,目标的任何组件节点也能处理。
4.注意HTMLCollection。任何时候访问HTMLCollection,不管属性还是方法,都是在文档上进行查询,开销比较昂贵。。要尽量最小化访问。优化的重点部分是循环。如:
1 var image=document.getElementsByTagName("img"), 2 i, 3 len; 4 for(var i=0,len=image.length;i<len;i++){ 5 //处理 6 }
优化的关键:长度length存入变量len,而不是每次都去访问HTMLCollection的length属性。在循环中使用HTMLCollection时,下一步应该是获取要使用项目的引用,避免在循环体内多次调用HTMLCollection。
1 var image=document.getElementsByTagName("img"), 2 i, 3 len; 4 for(var i=0,len=image.length;i<len;i++){ 5 img=image[i]; 6 }
这样,在循环中就没有理由再去访问image的HTMLCollection。
注意:在编写JavaScript时,要清楚何时返回HTMLCollection对象。这样就可以最小化的访问。以下情况下均返回HTMLCollection对象:
- 调用getElementsByTagName()
- 获得元素的childNodes属性。
- 获取元素的attributes属性。
- 访问特殊集合,如document.forms、document.images等。