------------------2016-7-20更------------------
最近在看《高性能JavaScript》一书,里面当中,有讲很多提高js性能的书,正在看的过程中,记下做法以及原因,供以后学习参考:
1、将经常使用的对象成员、数组项、和域外变量存入局部变量
原因:数据存储位置对大地代码整体性能会产生重要的影响,直接变量和局部变量的访问速度快于数组和对象成员。因为局部变量位于作用域链的第一个对象中,全局变量位于作用域链的最后一环。变量在作用域链的位置越深,访问的时间就越长。
1 var doc = document; 2 var db = doc.body; 3 var odiv = doc.getElementById('div1');
2、避免使用with表达式,因为他改变了运行期上下文的作用域链。
3、同理with,也要注意使用try-catch,因为catch也会改变运行期上下文的作用域链。
4、嵌套成员变量会造成重大的性能影响,尽量少用。
5、DOM操作量化问题:
//在循坏中更新页面,问题所在:每次循环都对DOM元素访问了两次,一次是读取document.getElementById('here').innerHTML的内容,一次是修改它。 function changeDOM(){ for(var i=0; i < 15000; i++){ document.getElementById('here').innerHTML += 'a'; } } //改变方法,使用局部变量存好改变量,在循环结束时一并修改 function changeDOM(){ var content =''; for(var i=0; i < 15000; i++){ content += 'a'; } document.getElementById('here').innerHTML += content; } //关于js字符串拼接的性能优化问题,js的处理机制是:新建一个临时字符串,将新字符串赋值为 content + 'a' ,然后返回这个新字符串并同时销毁原始字符串。导致字符串的连接效率较低的重要原因不仅在于对于新的临时变量的不断创建,还有js的垃圾回收机制下不断在对象创建期间回收,导致的效率低下。提高效率的办法是用数组的join函数: function changeDOM(){ var content =[]; for(var i=0; i < 15000; i++){ content.push('a'); } document.getElementById('here').innerHTML += content.join(''); } //但是同时也要注意,后来的大部分浏览器都对“+”的连接字符串做了优化,由于SpiderMonkey等引擎对字符串的“+”运算做了优化,结果使用Array.join的效率反而不如直接用“+”!,因此建议是:在IE7以下,使用join,在新浏览器下,除了变量缓存外,不需要做别的优化
6、克隆已有的DOM元素,即element.cloneNode(),比起新建节点来说,即element.createElement(),会快一点,但是性能提高不是很大。
7、遍历数组明显快于同样大小和内容的HTML集合
8、 for循环时,HTML某元素集合的长度不建议直接作为循环终止条件,最好将集合的长度赋给一个变量,然后使用变量作为循环终止条件;
原因:当每次迭代过程访问集合的length时,它导致集合器更新,在所有的浏览器上都会产生明显的性能损失。
9、需要考虑实际情况的优化,根据7,可以将集合中的元素通过for循坏赋值到数组中,访问数组的数组快于集合。但是要注意对于复制的开销是否值得。
1 function toArray(collection){ 2 var arr = []; 3 var clen = collection.length; 4 for(var i= 0; i < clen; i++){ 5 arr[i] = collection[i]; 6 } 7 8 }
10、获取DOM节点,使用nextSibling方式与childNodes方式,在不同的浏览器中,这两种方法的时间基本相等。但是在IE中,nextSibling比childNodes好,IE6下,nextSibling比对手快16倍,在IE7下,快105倍。因此,在老的IE中性能严苛的使用条件下,用nextSibling较好。
11、querySelectorAll()可以联合查询,即querySelectorAll(‘div .warning,div .notice’),在各大浏览器中支持也挺好的,还可以过滤很多非元素节点;
ps:这个网站是:http://caniuse.com/,可以检查HTML、CSS元素在各大浏览器的兼容情况,一个很有用的网站!
-------------2016-7-21更---------------
12、重绘和重排版;
重绘:不需要改变元素的长度和宽度,不影响DOM的几何属性;
重排版:影响了几何属性,需要重新计算元素的几何属性,而且其他元素的几何属性有可能也会受影响。浏览器会在重排版过程中,重新绘制屏幕上受影响的部分。
获取布局信息的操作将导致刷新队列的动作,如使用:offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、clientTop、clientLeft、clientHeight、geteComputedStyle()(在IE中此函数成为currentStyle);浏览器此时不得不进行渲染队列中带改变的项目,并重新排版以返回正确值。
解决办法:
a、通过延迟访问布局信息避免重排版。
b、整体修改cssText的css代码,而不是分开访问,修改cssText的属性
1 //访问了4次DOM,第二次开始重排列并强迫渲染队列执行 2 var el = document.getElementById('div1'); 3 el.style.borderLeft = '1px'; 4 el.style.borderRight = '2px'; 5 el.style.padding = '5px'; 6 //改进:改变合并,通过cssText实现 7 var el = document.getElementById('div1'); 8 el.cssText += 'border-left = 1px;border-right = 2px;padding = 5px;';
c、改变css类名来实现样式改变
d、当对DOM元素进行多次修改时,可以通过以下的步骤减少重绘和重排版的次数:
(注意:此过程引发两次重排版,第一次引发一次,第三次引发一次。如果没有此步骤的话,每次对第二步的改变都有可能带来重排版。)
- 从文档流中摘除该元素,摘除该元素的方法有:
- 使其隐藏,进行修改后在显示
- 使用文档片段创建子树,在将他拷贝进文档
1 var doc = document; 2 //创建文档子树 3 var frag = doc.createDocumentFragment(); 4 //自定义函数,将修改内容data赋给文档片段frag,具体过程忽略 5 appendDataToElement(frag,data); 6 //注意:添加时实际添加的是文档片段的子节点群,而不是frag自己,只会引发一次重排版 7 doc.getElementById('div1').appendChild(frag);
- 创建一个节点的副本,在副本上进行修改,再让复制节点覆盖原先节点
// 创建一个节点的副本,在副本上进行修改,再让复制节点覆盖原先节点 var oldNode = document.getElementById('old'); var clone = old.cloneNode(); appendDataToElement(clone,data); oldNode.replaceChild(clone,oldNode);
ps:推荐第二种,因为其涉及最少数量的操作和重排列。
- 对其应用多重改变
- 将元素带回文档中