JS高性能 from《高性能Javascript》
总结此书中提到的,写代码需注意,或者可优化的点。
可能不全面。欢迎讨论
加载和执行
Javascript下载和执行
由于浏览器只有一个UI线程,Javascript执行会阻塞UI渲染
1 script标签尽量放在body底部,以减少对整个页面下载的影响。
2 合并多个Javascript文件,减少http请求
3 无阻塞脚本:页面下载完成之后再下载Javascript文件
- 延迟脚本:
<script defer>
,指明本元素所含的脚本不会修改DOM。在onload事件触发前执行
部分浏览器不支持
- 动态脚本元素:
document.createElement("script")
,然后appendTo head中
加载顺序很重要
- XMLHttpRequest脚本注入:通过xhr下载Javascript文件,然后appentTo body中
- 研究webpack:压缩,懒加载,等等其他策略
4 CDN
数据存取
数据的读取:字面量、本地变量、数组元素、对象成员
1 尽量使用局部变量,如果引用了非局部变量,则在局部缓存该变量,比如var doc=document
2 No eval、new Function这种动态作用域
3 字面量/局部变量的存取速度 > 数组/对象成员的 存取速度
4 嵌套成员 嵌套越深,耗时越多。location.href
比window.location.href
性能更优
DOM编程
1 JS操作DOM,天生就慢,尽量避免
2 innerHTML
vs createElement
:差不多,推荐innerHTML
3 克隆DOM element.cloneNode()
略优于 document.createElement()
4 遍历一个HTML collections 比遍历一个数组还要耗时。所以
document.getElementsByName()
等等方式返回的dom节点的类数组对象
- 可以先拷贝到数组中再遍历
- 局部变量,先缓存
5 元素节点: children
优于 childNodes
,因为nodes包括注释、空格等等
6 选择器:大量组合查询时,querySelectorAll
优于getElementBy...
等
7 最小化重绘和重排
- 合并样式更新,比如利用添加移除class
- 批量修改dom
(1)元素脱离文档流: 隐藏/ 创建dom framgment/ clone
(2) 应用元素改变
(3)将改变后的元素带回文档中: 显示/ append/ innerHTML
8 缓存布局信息,不要频繁获取元素高宽度等信息
9 减少hover,耗费大量CPU计算
10 事件委托:利用冒泡,因为
事件绑定 占用处理时间
浏览器跟踪每个事件处理器,占内存
算法和流程控制
循环
1 No for-in
2 减少查找次数:比如缓存length
3 使用倒序,因为终止条件非0为真,而正序时<length,比较是两次,先判断大小,再判断真假。
4 减少迭代次数:“Duff's Device’”
while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
...
}
5 基于函数的迭代(eg.Array.forEach
)不如loops(eg.for
)
条件语句
1 条件多时用switch(vs if-else
)
2 最可能出现条件放在最前面
3 使用嵌套if-else
,而不是链式
像二分法一样,逐步缩小范围
4 大量离散值,使用查找表,即对象或数组,明确的对应关系
连续分块的,可能3更合适
递归
1 堆栈溢出一般都是因为不正确的终止条件
2 优化为尾递归,因为es6有尾递归优化
3 递归 改为 迭代实现
4 缓存遍历中的结果,避免重复遍历
字符串和正则表达式
字符串
1 str = str + "one" + "two"
优于str+="one";str+="two";
优于str+="one"+"two"
第一种最优的原因是浏览器内存分配的优化,不一定所有浏览器都适用
部分浏览器可能会在编译期就对"one"+"two"
进行合并了。
2 数组项合并Array.join()
耗时大于字符串拼接
IE7反之
3 string.concat
比 + +=
耗时多
正则表达式
1 具体化,能多具体写多具体,减少匹配失败成本和回溯
2 原子组,Javascript中可以用预查 (?=)
3 尽量避免嵌套量词:减少回溯
4 确保正则表达式的两个部分不能对字符串的相同部分进行匹配:减少回溯
5 以简单、必须的字元开始
6 字元互斥,同4
7 减少分支数量,缩小分支范围:比如red|raw
-> r(?:ed|aw)
8 使用非捕获组:捕获组消耗时间和内存来记录反向引用,不需要反向引用的就可以避免
9 补充8,只捕获感兴趣的文本以减少后处理
10 暴露必须的字元,同1
11 合适的量词,减少回溯
12 将正则表达式赋值给变量并重用它们:主要是防止循环体重重复编译正则表达式
13 拆分复杂正则表达式
14 能简单通过字符串的方法实现就不要用正则
15 去除首尾空白的例子,非常经典具体,最后混合使用字符串方法和正则达到性能最优
快速响应用户界面
解决脚本执行阻塞用户界面更新
记录耗时,针对性的解决
1 使用定时器让出时间片段:需要限制定时器数量,否则性能更差
2 分割任务
3 web worker
Ajax
1 利用Beacons,无需response。
new image().src=url+'?'+params.join('&')
,google、百度 analysis好像都是这么做的
2 缓存:客户端和服务端
3 合并请求,减少请求数
其他
1 使用Object Array 直接量
2 避免重复工作
3 LazyLoading
4 条件预加载,调用更快。与3正相反,适用不同的场景
5 位操作性能更优,比如取模可以用位操作
6 原生方法 性能更优