0. 前言
js解释性代码,编译慢。。。V8,chrome2008年发布,速度快,把JS转化为机器码来执行
1. 加载和执行
JS阻塞特性:当浏览器在执行Javascript代码时,不能同时做其他任何事情。
大多浏览器都使用单一进程来处理用户界面UI更新和Javascript脚本执行,页面的下载和渲染都必须停下来等待脚本执行完成
1.1 脚本位置
浏览器在解析到 <body>
标签之前,不会渲染页面的任何部分
每个文件必须等到前一个文件下载并执行完成才会开始下载(IE8、Firefox3.5、Safari4、Chrome2都允许并行下载JS文件,虽然脚本下载过程不会相互影响,但会阻塞其他资源下载,比如图片)
推荐:所有的 <script>
标签尽可能放到 <body>
标签的底部(雅虎特别性能小组提出)
1.2 组织脚本
每遇到一个 <script>
标签,都会执行脚本而导致一定的延时。请求HTTP请求带来的额外性能开销,因此下载单个100KB文件比下载4个25KB的文件更快
解决:多个文件合并一个,可通过离线的打包工具
雅虎提供了合并处理器,以通过CDN分发:<script type="text/javascript" src="http://example.demo.com?build/example1-min.js&build/example2-min.js"></script>
1.3 无阻塞的脚本
JS倾向阻止浏览器的某些处理,最显著性能问题:HTTP请求和用户界面更新。减少JS大小并限制HTTP请求数仅仅是开始
在window对象的load事件触发下再下载脚本
1.3.1 延迟脚本
<script>
标签定义了一个扩展属性:defer
。页面解析到此<script>
标签时开始下载,但不会执行,直到DOM加载完成(onload事件被触发前)
1.3.2 动态脚本元素
技术重点:无论何时启动下载,文件的下载和执行过程不会阻塞页面其他进程
Firefox,Opera,Chrome和Safari3以上版本会在<script>
元素接收完成时触发一个load事件,script.onload = func(){}
成为最通用的无阻塞加载解决方案
1.3.3 XMLHttpRequest脚本注入
主要优点:下载JS代码但不立即执行
主要局限:JS文件必须与所请求的页面处于相同的域
1.3.4 推荐的无阻塞模式
添加大量JS做法:先添加动态加载所需的代码,然后加载初始化页面所需的剩下的代码
YUI3的方式:YUI().use("dom",function(Y){Y.DOM.addClass(document.body, "loaded"); });
LazyLoad类库:loadScript()函数增强版,1.5KB
LABjs:允许使用wait()方法来指定哪些文件需要等待其他文件
2. 数据访问
JS的四种基本的数据存储位置:直接量、变量、数组元素、对象成员
访问数组元素和对象成员的代价更高一些。Firefox优化数组项的存取
建议:在乎运行速度,尽量使用直接量和局部变量,减少数组项和对象成员的使用
2.1 管理作用域
2.1.1 作用域链和标识符解析
内部属性[[Scope]]
包含了一个函数被创建的作用域中对象的集合
活动对象作为函数运行期的可变对象,包含了所有局部变量,命名参数,参数集合以及this
2.1.2 标识符解析的性能
函数中读写局部变量总是最快的。对所有浏览器而言,总趋势是一个标识符所在的位置越深,它的读写速度也就越慢。
经验法则:如果某个跨作用域的值在函数中被引用一次以上,那么就把它存储到局部变量里;先将全局变量的引用存储在一个局部变量中,然后使用这个局部变量代替全局变量
2.1.3 改变作用域链
有两个语句可以在执行时临时改变作用域链,第一个是with,第二个是catch
2.1.4 动态作用域
eval()的函数,认为是动态作用域。
2.1.5 闭包,作用域和内存
因为闭包的[[Scope]]
属性包含了与运行期上下文作用域链相同的对象的引用,因此会有一项副作用。通常来说,函数的活动对象会随同运行期上下文一同销毁。
脚本中的闭包与非闭包函数相比,需要更多的内存开销。
2.2 对象成员
包括属性(引用了非函数类型的成员)和方法(引用了一个函数)
2.2.1 原型
JavaScript中的对象是基于原型的。“类“定义了创建新对象的过程。而原型对象为所有对象实例所共享
对象可以有两种成员类型:实例成员和原型成员。方法:hasOwnProperty()和in操作符
2.2.2 原型链
对象的原型决定实例的类型,所有对象都是对象的实例,并继承了所有基本方法
对象在原型链中存在的位置越深,找到它也就越慢
2.2.3 嵌套成员
每次遇到点操作符,嵌套成员会导致JS引擎搜索所有对象成员:执行location.href
总是比window.location.href
要快
2.2.4 缓存对象成员值
在函数中如果多次读取同一个对象属性,最佳做法是将属性值保存到局部变量中
3. DOM编程
3.1 浏览器中的DOM
浏览器中通常会把DOM和JavaScript独立实现
3.1.1 天生就慢
DOM和JS就是各为一个岛屿,它们之间用收费桥梁连接
3.2 DOM访问与修改
效率高版本:使用局部变量存储更新后的内容,然后在循环结束后一次性写入
3.2.1 innerHTML对比DOM方法
innerHTML和原生DOM方法生成HTML表格的结果对比,innerHTML的优势更加明显(分浏览器)。
3.2.2 节点克隆:大多数浏览器更有效率
3.2.3HTML集合:getElementByName、getElementByClassName、getElementsByTagName
返回类似数组的列表
昂贵的集合:在循环的条件控制语句中读取数组的length属性是不推荐的做法。读取一个集合的length比读取普通数组的length要慢得多,因为每次都要重新查询
优化方法:把集合的长度缓存到一个局部变量中,然后在循环的条件退出语句中使用该变量
toArray()函数可作为一个通用的集合转数组函数
访问集合元素时使用局部变量
3.2.4 遍历DOM
在DOM中爬行:childNodes得到元素集合,nextSiblings来获取每个相邻元素
元素节点:尽量使用浏览器提供的API,它们执行效率比自己在JS中过滤的效率要高
选择器API:querySelectorAll()原生方法,比DOM遍历快多。
3.3 重绘与重排
下载完所有组件(HTML、JS、CSS、图片)后解析生成:DOM树(表示页面结构)、渲染树(表示DOM节点如何显示)
3.3.1 重排何时发生?
主要是页面变化,滚动条的出现
3.3.2 渲染树变化的排队与刷新
局部信息的操作会导致列队刷新:offset、scroll、client、getComputedStyle()
3.3.3 最小化重绘和重排
代价昂贵,策略是减少此类操作发生
改变样式:cssText属性,一次重写
修改CSS的class名称
批量修改DOM:使元素脱离文档流、对应用多重更改、把元素带回文档中
减少一个重排的方法:改变display属性、临时从文档中移除元素;在文档外创建并更新一个文档片段,再附加到原始列表中;为需要修改的节点创建一个备份,然后对副本进行操作,完成后替代久节点
3.3.4 缓存布局信息:var current = myElement.offsetLeft
3.3.5 让元素脱离动画流
3.3.6 IE和:hover 大量使用,降低相应速度
3.4 事件委托:以此减少事件处理器的数量
4. 算法和流程控制
4.1 循环
for-in循环比其他循环慢,其他三种:for、while、do-while
减少迭代的工作量:通过颠倒数组的顺序来提高循环性能
减少迭代次数:Duff‘s Device
原生数组方法forEach(当前数组项的值、索引、数组本身)
4.2 条件语句
switch和if-else:大多数情况下switch比if-else运行得要快,只有条件数量很大时才快的明显;if-else适用于判断两个离散值或几个不同的值域,当判断多个两个离散值时,swtich语句是更佳选择
if-else优化:确保最可能出现的条件放首位、使用二分法把值域分成一系列区间逐步缩小范围
当使用查找表时,必须完全抛弃条件判断语句
4.3 递归
遇到栈大小限制时:直接递归(函数的自身调用)、“隐伏模式“(两个函数相互调用,形成无限循环)
合并排序算法是最常见的用递归实现的算法
减少工作量是最好的性能优化技术:缓存前一个计算结果后续计算使用避免重复工作
5. 字符串和正则表达式(太高深,感觉项目中用得少,总结几个点)
大多浏览器中,数组项连接比其他字符串连接方法更慢
IE7“天真“的连接算法
concat比使用简单的+和+=稍慢
6. 快速响应的用户界面
任何JS任务都不应当执行超过100毫秒,过长的运行导致UI更新出现延迟,从而影响体验
JS运行期间,浏览器响应用户交互的行为存在差异
定时器可用来代码延迟执行,把长时间运行脚本分解成一系列小任务
web worker允许在UI线程外部执行JS代码,从而避免锁定UI
7. Ajax
7.1 数据传输
XMLHttpRequest:POST和GET对比
动态脚本注入
Multipart XHR
7.2 数据格式
XML、JSON、JSON-P、HTML、自定义格式
格式越轻越好,JSON和字符分隔自定义格式是最好的
7.3 Ajax性能指南
缓存数据:服务端设置头:Expires、客户端:存本地
准则:减少请求数、缩短页面的加载时间、确保代码错误不会输出给用户、使用成熟的Ajax类库
8. 编程实践
8.1 避免双重求值
eval()、Function()构造函数、setTimeOut()、setInterval()
8.2 使用Object/Array直接量
8.3 不要重复工作
延迟加载:原始函数包含被包含正确操作的新函数覆盖
条件预加载:脚本加载时检测而不是加载后
8.4 使用速度快的部分
位操作:计算操作发生在系统底层,如果多项保存在一起并频繁检查,位掩码有助于提高整体性能
原生方法:无论怎么优化,不可能比JS引擎提供的原生方法更快。如Math函数能用就用
9. 构建并部署高性能JavaScript应用(后端知识较多,比较欠缺,总结几个点吧)
合并JS文件减少HTTP请求数
使用工具压缩JS文件
在服务器端压缩JS文件(Gzip编码)
正确设置HTTP响应头缓存JS文件,增加时间戳避免缓存问题
使用CDN提供JS文件:提升性能、管理文件的压缩和缓存
10. 工具(略,很多工具偏老,不推荐也不说了)
参考他人的总结