1. Javascript 性能优化总结<br />Out-of-the-flow DOM Manipulation(在页面外进行DOM操作)<br />This pattern lets us create multiple elements and insert them into the DOM triggering a single reflow。<br />如果有多个对DOM的操作,我们最好先将元素从DOM树中剥离开来,再对这个元素进行一系列的操作之后,最后再通过appendChild或者insertBefore插入原来位置的DOM树中。这样就使得页面的reflow的次数最小化。<br />问题: <br />The problems is, this can cause a reflow for each anchor。每设置个className都导致一次Reflow.<br />function updateAllAnchors(element, anchorClass) {<br /> var anchors = element.getElementsByTagName('a');<br /> for (var i = 0, length = anchors.length; i < length; i ++) {<br /> anchors[i].className = anchorClass;<br /> }<br />}<br />解决方案:<br />为了解决这个问题,我们可以先remove这些元素,然后更新这些元素,最后再<br />把元素插入到元素原来的位置。<br />* Remove an element and provide a function that inserts it into its original position<br /> * @param element {Element} The element to be temporarily removed<br /> * @return {Function} A function that inserts the element into its original position<br /> **/<br />function removeToInsertLater(element) {<br /> var parentNode = element.parentNode;<br /> var nextSibling = element.nextSibling;<br /> parentNode.removeChild(element);<br /> return function() {<br /> if (nextSibling) {<br /> parentNode.insertBefore(element, nextSibling);<br /> } else {<br /> parentNode.appendChild(element);<br /> }<br /> };<br />function updateAllAnchors(element, anchorClass) {<br /> var insertFunction = removeToInsertLater(element);<br /> var anchors = element.getElementsByTagName('a');<br /> for (var i = 0, length = anchors.length; i < length; i ++) {<br /> anchors[i].className = anchorClass;<br /> }<br /> insertFunction();}<br /> DocumentFragment DOM Generation (利用DocumentFragment产生DOM)<br />We create a DocumentFragment outside of the DOM (so it is out-of-the-flow),这样可以页面一次Reflow.<br />例如,我们需要一次插入多个html标签,<br />function addAnchors(element) {<br /> var anchor;<br /> for (var i = 0; i < 10; i ++) {<br /> anchor = document.createElement('a');<br /> anchor.innerHTML = 'test';<br /> element.appendChild(anchor);<br /> }<br />}<br />我们应该如下写:<br />function addAnchors(element) {<br /> var anchor, fragment = document.createDocumentFragment();<br /> for (var i = 0; i < 10; i ++) {<br /> anchor = document.createElement('a');<br /> anchor.innerHTML = 'test';<br /> fragment.appendChild(anchor);<br /> }<br /> element.appendChild(fragment);<br />}<br />CSS class switching DOM manipulation(DOM 操作css 样式)<br />对DOM的css操作,每操作一次,都会导致一次页面的reflow.<br />例如: function selectAnchor(element) {<br /> element.style.fontWeight = 'bold';<br /> element.style.textDecoration = 'none';<br /> element.style.color = '#000';<br />}<br />应该如下写:<br />.selectedAnchor {<br /> font-weight: bold;<br /> text-decoration: none;<br /> color: #000;<br />}<br />function selectAnchor(element) {<br /> element.className = 'selectedAnchor';<br />}<br />4.Single Element DOM Generation<br />This pattern lets us create and add a single element to the DOM triggering a single reflow. After creating the element, make all changes to the new element before adding it to the DOM.<br />在创建一个元素之后,先对元素设置属性之后在增加dom节点中。<br />例如: function addAnchor(parentElement, anchorText, anchorClass) {<br /> var element = document.createElement('a');<br /> parentElement.appendChild(element);<br /> element.innerHTML = anchorText;<br /> element.className = anchorClass;<br />}<br />应该写成: <br />function addAnchor(parentElement, anchorText, anchorClass) {<br /> var element = document.createElement('a');<br /> element.innerHTML = anchorText;<br /> element.className = anchorClass;<br /> parentElement.appendChild(element);<br />}<br />5.循环的使用技巧<br />onload = function(){ Watch.start(quot;
Store lengthquot;
); var p2 = document.getElementsByTagName(quot;
pquot;
); //第一次循環使用 for (var i = 0, l = p2.length; i < l; i++) { p2[i].innerHTML = quot;
Change againquot;
; } //第二次循環使用 for (var i = 0, l = p2.length; i < l; i++) { p2[i].innerHTML = quot;
Change againquot;
; } Watch.stop(); /////////////////////////////////////////////////////////////////////// Watch.start(quot;
Store to arrayquot;
); var p = document.getElementsByTagName(quot;
pquot;
); var arr = []; var c = null; //儲存進數組 //注意這種循環的使用方式,下同 for (var i = 0; c = p[i++];) { arr.push(c); } //第一次循環使用 for (var i = 0; c = arr[i++];) { c.innerHTML = quot;
Change backup.quot;
} //第二次循環使用 for (var i = 0; c = arr[i++];) { c.innerHTML = quot;
Change backup.quot;
} Watch.stop(); //打印结果 Watch.report();}<br />第二种方法的效率大约是第一种方法的两倍。<br />而对于将,比如改变结束条件判断的方式,不根据长度(length)递增的形式,而是使用递减的方式,这个经过测试,在一定程度上能提高循环的性能,比如(for(var i=length-1;i>=0;i–),但实际上却差不多。<br />6.缩短作用域链(减少闭包)<br />缩短作用域链,说简单点就是尽量使用局部变量来储存外部的对象。对于在循环中重复出现的对象,这个尤其具有优化效果;对于在闭包函数外的对象,当需要在闭包内使用的时候,可以在闭包内声明一个局部变量来储存该对象;还有对DOM元素的length属性、或者在DOM操作的循环中单个DOM元素使用局部变量来储存,也十分具有优化效果。目的就是使得在数据存取的过程中从作用域链中能最快的取出来,而最快的取出来的前提就是需要对象或者变量在作用域链的顶部。经过测试表明:局部变量的存取速度是最快的。所以将作用域外的对象局部引用化,也是不错的优化方式。<br />例子一:将NodeList的length属性缓存起来,这样就可以避免每次重新计算length属性,重新查询一遍DOM树。var d = document.getElementsByTagName(quot;
divquot;
);for(var i=0, l = d.length; i<l; i++){ //code here...}<br />//例子二:将每一个DOM元素局部化,因为对每一个DOM元素的操作,都需要在NodeList中去查询这个DOM元素。var d = document.getElementsByTagName(quot;
divquot;
);for(var i=0, l = d.length; i<l; i++){ var item = d[i]; item.className=quot;
activequot;
; item.title = quot;
active itemquot;
; //......}<br />//例子三:储存重复出现的对象或者方法,特别是在循环中。将document.body缓存起来,这样在循环里读取的速度就非常快了。var b = document.body;for(var i=0;i<10;i++){ b.appendChild(document.createTextNode(quot;
some textquot;
));}<br />//=====================================//例子四:对闭包外的对象或者变量在闭包内局部化。//msg在setTimeout闭包参数的作用域外function setupAlertTimeout() { var msg = 'Message to alert';window.setTimeout(function() { alert(msg); }, 100);}//下面这个更快,将变量声明放到闭包内,使得读取速度更快。function setupAlertTimeout() { window.setTimeout(function() { var msg = 'Message to alert'; alert(msg); }, 100);}//再来个更快的,这下避免使用闭包来作为setTimeout的参数,使得程序运行更快。function alertMsg() { var msg = 'Message to alert'; alert(msg);}function setupAlertTimeout() { window.setTimeout(alertMsg, 100);} <br />对于上面例子四中关于闭包的使用,在这里解释一下:闭包非常强大而且使用,但是它也有很多缺陷,比如:是造成很多内存泄漏的罪魁祸首;声明一个闭包比声明一个不是闭包的内部函数更慢,比声明一个静态函数就更慢了。因此得出了上面的结论。<br />还有,对于缩短作用域链的一个更直白的解释例子如下:<br />var a = 'a';function createFunctionWithClosure() { var b = 'b'; return function () { var c = 'c'; a; b; c; };}var f = createFunctionWithClosure();f();<br />如上所示:当函数f调用的时候,读取a比读取b慢,比读取c又更慢。这就是作用域链的存取速度导致的性能问题,在HTMLCollection、NodeList中更甚。<br />