高性能Javascript读书总结
1. 加载和执行
- 尽量将所有的
<script>
标签放在</body>
标签之前,确保脚本执行前页面已经完成了渲染,避免脚本的下载阻塞其他资源(例如图片)的下载。 - 合并脚本,减少页面中的
<script>
标签 - 使用
<script>
标签的defer
和async
属性(两者的区别见这里)
- 通过Javascript动态创建
<script>
标签插入文档来下载,其不会影响页面其他进程
2.数据存取
- 由于作用域链的机制,访问局部变量比访问跨作用域变量更快,因此在函数中若要多次访问跨作用域变量,则可以用局部变量保存。
- 避免使用
with
语句,其会延长作用域链 - 嵌套的对象成员会导致引擎搜索所有对象成员,避免使用嵌套,例如
window.location.href
- 对象的属性和方法在原型链的位置越深,访问的速度也越慢
3.Dom编程
- 进行大段HTML更新时,推荐使用
innerHTML
,而不是DOM方法 - HTML集合是一个与文档中元素绑定的类数组对象,其长度随着文档中元素的增减而动态变化,因此避免在每次循环中直接读取HTML集合的
length
,容易导致死循环 - 使用节点的
children
属性,而不是childNodes
属性,前者访问速度更快,且不包含空白文本和注释节点。 - 浏览器的渲染过程包括构建DOM树和渲染树,当DOM元素的几何属性变化时,需要重新构造渲染树,这一过程称为“重排”,完成重排后,浏览器会重新绘制受影响的部分到屏幕中,这一过程称为“重绘”。因此应该尽量合并多次对DOM的修改,或者先将元素脱离文档流(
display:none
、文档片段),应用修改后,再插入文档中。 - 每次浏览器的重排时都会产生消耗,大多数浏览器会通过队列化修改并批量执行来优化重排过程,可当访问元素
offsetTop
、scrollTop
、clientTop
、getComputedStyle
等一系列布局属性时,会强制浏览器立即进行重排返回正确的值。因此不要在dom布局信息改变时,访问这些布局属性。 - 当修改同个元素多个Css属性时,可以使用CssText属性进行一次性修改样式,减少浏览器重排和重绘的次数
- 当元素发生动画时,可以使用绝对定位使其脱离文档流,动画结束后,再恢复定位。避免动画过程中浏览器反复重排文档流中的元素。
- 多使用事件委托,减少监听事件
4.算法和流程控制
for
循环和while
循环性能差不多,除了for-in
循环最慢(其要遍历原型链)- 循环中要减少对象成员及数组项的查询次数,可以通过倒序循环提高性能
- 循环次数大于1000时,可运用
Duff Devices
减少迭代次数 switch
比if-else
快,但如果具有很多离散值时,可使用数组或对象来构建查找表- 递归可能会造成调用栈溢出,可将其改为循环迭代
- 如果可以,对一些函数的计算结果进行缓存
5.字符串和正则表达式
- 进行大量字符串的连接时,
+
和+=
效率比数组的join
方法要高 - 当创建了一个正则表达式对象时,浏览器会验证你的表达式,然后将其转化为一个原生代码程序,用户执行匹配工作。当你将其赋值给变量时,可以避免重复执行该步骤。
- 当正则进入使用状态时,首先要确定目标字符串的起始搜索位置(字符串的起始位置或正则表达式的
lastIndex
属性),之后正则表达式会逐个检查文本和正则模式,当一个特定的字元匹配失败时,正则表达式会试着回溯到之前尝试匹配的位置,然后尝试其他路径。如果正则表达式所有的可能路径都没有匹配到,其会将起始搜索位置下移一位,重新开始检查。如果字符串的每个字符都经历过检查,没有匹配成功,则宣布彻底失败。 - 当正则表达式不那么具体时,例如
.*
和[\s\S]*
等,很可能会出现回溯失控的情况,在js中可以应用预查模拟原子组(?=(pattern))\1
来避免不必要的回溯。除此之外,嵌套的量词,例如/(A+A+)+B/
在匹配”AAAAAAAA”时可能会造成惊人的回溯,应尽量避免使用嵌套的量词或使用预查模拟原子组消除回溯问题。 - 将复杂的正则表达式拆分为多个简单的片段、正则以简单、必需的字元开始、减少分支数量
|
,有助于提高匹配的效率。
6.快速响应的用户界面
- 单个JavaScript运算操作时间不应该超出100ms,否则可能会阻塞用户操作
如果要执行长时间的运算,可以通过定时器将计算过程分割成多个步骤,使UI可以得到更新,例如
setTimeout(function(){
process(todo.shift());
if (todo.length > 0) {
setTimeout(arguments.callee, 25);
} else {
callback();
}
})
较长时间的计算过程也可以按照代码运行的时间进行分割,每次控制运行的时间,例如
setTimeout(function(){
let start = +new Date();
do {
process(todo.shift());
} while(todo.length > 0 && (+new Date() - start) < 50)
if (todo.length > 0) {
setTimeout(arguments.callee, 25);
} else {
callback();
}
})
高频率重复的定时器数量尽量要少,建议使用一个独立的重复定时器
- 使用
WebWork
进行计算
7. AJAX
设置HTTP头部信息进行缓存,例如
Expires: Mon,28 Jul 2018 23:30:30 GMT
对于一些函数的计算结果进行本地缓存
8. 编程实践
- 避免使用
eval
、Function
进行双重求值 - 使用
Object
/Array
字面量定义,不要使用构造函数 - 使用延迟加载消除函数中重复的工作
使用位操作,例如与1进行按位与计算,得到奇偶交替
if (i & 1) {
className = 'odd';
} else {
className = 'even';
}
多使用JS内置的原生方法,例如
Math
对象等
9.构建和部署
- 合并、压缩多个js文件
- 设置HTTP缓存
- 使用内容分发网络CDN
10.性能分析工具
这本书的分析工具似乎有些已经过时了,这里推荐使用chrome浏览器自带的performance
模块进行分析,感觉很不错,可以参考这篇文章
如果觉得这篇文章帮助了您,请打赏一个小红包鼓励作者继续创作哦!!!
还没有评论,来说两句吧...