<高性能JavaScript>笔记 [1~3]
<高性能JavaScript>这是一本很好的书,主要讨论实际js开发时要注意的性能问题.
也算是一本JS的进阶读物,如果要看此书,建议先看<JavaScript权威指南>
一:加载和执行
主要讲述了如何更高效的载入JavaScript脚本文件,解决浏览器的阻塞问题.
其中最简单的解决方法就是把导入的js文件的标签写在 </body> 前,而且不需要使用 onload 事件.
尽量把js文件进行压缩和合并,因为每次加载一个外链js文件都需要发送一次http请求,这样会增加性能的消耗.
另外,每一个 <script> 标签初始下载时都会堵塞页面渲染,这不仅针对外链脚本,内嵌脚本也有同样的情况,每遇到一个 <script> 标签就会因执行脚本而导致一定的延时,所以减少<script> 标签能改善页面的总体性能.
当把内嵌脚本放在引用外联样式表的 <link> 标签之后会导致页面堵塞去等待样式表的下载,这样做是为了确保内嵌脚本在执行时能获得最精准的样式信息,因此不建议把内嵌脚本紧跟在<link> 标签之后.
那什么是无阻塞脚本呢?
window 对象的 onload 事件触发后才下载脚本,就是所谓的无堵塞脚本.因为这个时候下载脚本不会影响页面的下载和渲染.
解决方法有:
+ 延迟脚本(Deferred Script) - 使用 <script> 的 defer 属性(仅适用于IE和Firefox3.5+);
+ 动态脚本元素(Dynamic Script Elements) - 使用动态创建的 <script> 元素来下载并执行代码;
+ XMLHttpRequest脚本注入(XMLHttpRequest Script Injection) - 使用 XMLHttpRequest 对象下载js代码并注入页面;
最后,
还介绍了YUI3,LazyLoad,LABjs等库来实现无阻塞脚本.
二:数据访问
- 访问直接量和局部变量的速度最快,相反,访问数组元素的对象成员相对较慢;
- 由于局部变量存在于作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快.变量在作用域链中的位置越深,访问所需时间就越长.由于全局变量总处在作用域链的最末端,因此访问速度也是最慢的;
- 避免使用 with 语句,因为它会改变运行期上下文作用域链.同样, try-catch 语句中的 catch 也有同样的影响,因此也要小心使用;
- 嵌套的对象成员会明显影响性能,尽量少用. 如: location.href 比 window.location.href 要快;
- 属性或方法在原型链中的位置越深,访问它的速度也越慢;
- 通常来说,你可以通过把常用的对象成员,数组元素,跨域变量保存在局部变量中来改善JavaScript的性能,因为局部变量访问速度更快. 如: var div = document.getElementById("div");
这章用了很多UML图来解释JavaScript的数据访问,很具体形象.
三:DOM访问与修改
浏览器中通常会把DOM和JavaScript独立实现,分为两个不同的库.
因此访问DOM需要巨大的性能开销.
尽量避免DOM的访问次数,是提高性能的重要环节.
例如:多次访问的DOM节点可以通过局部变量来存储.
另外,要小心处理HTML集合.
以下方法和属性均返回一个HTML集合:
document.getElementByName()
document.getElementsByClassName()
document.getElementsByTagName()
document.all
document.images
document.links
document.forms
document.forms[0].elements
所谓的的HTML集合,是动态返回对应的HTML对象集合.
每一次调用以上方法和属性,都会重复一次查询元素的过程,开销十分大.
建议copy到一个数组对象中再进行操作.
许多现代浏览器均提供一些API来操作元素节点.
- 访问直接量和局部变量的速度最快,相反,访问数组元素的对象成员相对较慢;
- 由于局部变量存在于作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快.变量在作用域链中的位置越深,访问所需时间就越长.由于全局变量总处在作用域链的最末端,因此访问速度也是最慢的;
- 避免使用 with 语句,因为它会改变运行期上下文作用域链.同样, try-catch 语句中的 catch 也有同样的影响,因此也要小心使用;
- 嵌套的对象成员会明显影响性能,尽量少用. 如: location.href 比 window.location.href 要快;
- 属性或方法在原型链中的位置越深,访问它的速度也越慢;
- 通常来说,你可以通过把常用的对象成员,数组元素,跨域变量保存在局部变量中来改善JavaScript的性能,因为局部变量访问速度更快. 如: var div = document.getElementById("div");
这章用了很多UML图来解释JavaScript的数据访问,很具体形象.
三:DOM访问与修改
浏览器中通常会把DOM和JavaScript独立实现,分为两个不同的库.
因此访问DOM需要巨大的性能开销.
尽量避免DOM的访问次数,是提高性能的重要环节.
例如:多次访问的DOM节点可以通过局部变量来存储.
另外,要小心处理HTML集合.
以下方法和属性均返回一个HTML集合:
document.getElementByName()
document.getElementsByClassName()
document.getElementsByTagName()
document.all
document.images
document.links
document.forms
document.forms[0].elements
所谓的的HTML集合,是动态返回对应的HTML对象集合.
每一次调用以上方法和属性,都会重复一次查询元素的过程,开销十分大.
建议copy到一个数组对象中再进行操作.
许多现代浏览器均提供一些API来操作元素节点.
属性名 |
被替换的属性 |
children |
childNodes |
childElementCount |
childNodes.length |
firstElementChild |
firstChild |
lastElementChild |
lastChild |
nextElementSibling |
nextSibling |
previousElementSibling |
previousSibling |
以上属性Firefox 3.5+, Safari 4+, Chrome 2+, Opera 9.62+ 均支持.
但IE6,7,8只支持children属性.
还可以使用CSS选择器来获取特定元素对象.
最新的浏览器都提供了一个querySelectorAll()的方法.
例如: var elements = document.querySelectorAll("#menu a");
不过该方法返回的对象是一个NodeList的类数组对象,并不是HTML集合.
所以并不会重复查询元素.
IE 8+, Firefox 3.5+, Safari 3.1+, Chrome 1+, Opera 10+ 均支持该属性.
而 querySelector() 用来获取第一个匹配的节点.
尽量减少渲染树的强制刷新(关于DOM树和渲染树,请自行Google或查阅该书)
以下方法会导致渲染树的强制刷新:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (currentStyle in IE)
但IE6,7,8只支持children属性.
还可以使用CSS选择器来获取特定元素对象.
最新的浏览器都提供了一个querySelectorAll()的方法.
例如: var elements = document.querySelectorAll("#menu a");
不过该方法返回的对象是一个NodeList的类数组对象,并不是HTML集合.
所以并不会重复查询元素.
IE 8+, Firefox 3.5+, Safari 3.1+, Chrome 1+, Opera 10+ 均支持该属性.
而 querySelector() 用来获取第一个匹配的节点.
尽量减少渲染树的强制刷新(关于DOM树和渲染树,请自行Google或查阅该书)
以下方法会导致渲染树的强制刷新:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (currentStyle in IE)
以上属性和方法需要返回最新的布局信息,因为要强制渲染列队中的渲染树修改项.
在修改样式的过程中,最好避免使用上面列出的属性.
对于css样式的多次操作,可以通过cssText进行合并:
element.style.cssText = "border-left: 1px; padding: 5px;"
但此方法是直接覆盖已有的style样式,如果要追加样式:
element.style.cssText += "margin: 5px;"
当然,也可以通过修改class名称避免多次强制刷新
需要批量修改DOM时,可以通过通过下面的方法优化,借此减少强制刷新的次数:
+隐藏元素,应用修改,重新显示;
+使用文档片断进行构建子树,然后插入文档; (建议使用此方法 createDocumentFragment() )
+将原始元素拷贝到一个脱离文档的节点,修改副本在进行替换;
还要避免页面大部分元素的重排,可以让动画元素使用绝对定位脱离文档流.
善于利用时间委托,也是提高性能的好办法.
在修改样式的过程中,最好避免使用上面列出的属性.
对于css样式的多次操作,可以通过cssText进行合并:
element.style.cssText = "border-left: 1px; padding: 5px;"
但此方法是直接覆盖已有的style样式,如果要追加样式:
element.style.cssText += "margin: 5px;"
当然,也可以通过修改class名称避免多次强制刷新
需要批量修改DOM时,可以通过通过下面的方法优化,借此减少强制刷新的次数:
+隐藏元素,应用修改,重新显示;
+使用文档片断进行构建子树,然后插入文档; (建议使用此方法 createDocumentFragment() )
+将原始元素拷贝到一个脱离文档的节点,修改副本在进行替换;
还要避免页面大部分元素的重排,可以让动画元素使用绝对定位脱离文档流.
善于利用时间委托,也是提高性能的好办法.