读高性能JavaScript 总结:
先谈谈自己的感受,在前端行业高速发展的这几年,前端JS性能一直努力榨取,读本书后觉得性能问题可以从一下方面入手:
本书以章节形式总结:
第一章:加载和执行
- 在页面中script标签越少越好
- 尽量确保脚本执行前页面已经完成渲染
- 使用无阻塞方式下载javascript
3.1 使用script标签的 defer属性(适用于IE和firefox3.5以上版本)
3.2 使用动态创建的script元素来下载并执行代码
3.3 使用XHR对象下载javascript代码并注入页面中
第二章:数据访问
- 数据存储位置会影响读取速度 ,所以JavaScript有四种基本数据存取位置:直接量,变量 ,数组元素 ,对象成员 ,直接量和变量性能较好。
- 作用域 :分为 函数作用域,块级作用域 。 作用域理论概念:作用域就是变量与函数的可访问范围(拟物:作用域就是你的房间,你可以在你房间里 吃饭 睡觉 打豆豆(变量 ,函数),但你不能跑到别人房间里去吃饭睡觉 打豆豆。)
- 作用域链:内部属性是[[Scope]],内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问,默认作用域链中存在全局对象。如果在执行运行期上下文中,则会有自己的作用域链,且会创建一个活动对象,活动对象为函数运行期对象,包含局部变量,命名参数,参数集合 以及this,然后此对象呗推入作用域链的前端。当运行期上下文被销毁,活动对象也随之销毁。
- 标识符解析 :由作用域 和作用域链可知,标识符所在位置越深读写速度越慢,因此 ,函数中读写局部变量总是最快的,而读写全局变量通常是最慢的
全局变量总是存在与运行期 上下文作用域的最末端,因此它也是最远的
- 闭包:闭包允许函数访问局部作用域之外的数据。因此在闭包创建时,其作用域链会存在创建方法的作用域链的活动对象,在闭包被执行时,一个自身的运行期上下文被创建,它的作用域链与属性[[Scope]]中引用的两个相同的作用域链对象同时被初始化,然后一个活动对象为闭包自身所创建。
- 对象成员: 在面向对象编程中,函数中被命名的成员引用一个函数,该成员呗称为 “方法” ,引用了非函数类型成员,就成为“属性”。
6.1 原型: javascript 中的对象 是基于原型的。原型是其他对象的基础,对象通过一个内部属性绑定到它的原型,属性是__proto__ , 对象有两种成员类型:实例成员(own成员)和原型成员。
6.2 属性或方法在原型链中的位置越深,访问它的速度也越慢。
6.3 把常用的对象成员、数组元素、跨域变量保存在局部变量中来改善JavaScript性能,因为局部变量访问速度更快
第三章 DOM编程
访问和操作DOM是现代web应用的重要部分,每次进行JS和DOM交互均会产生性能损失,需记住以下几点:
- 最小化DOM访问次数,尽可能在JavaScript 端处理
- 如需多次访问某个DOM节点,请使用局部变量存储它的引用。
- 小心处理HTML集合,因为它实时联系着底层文档。把集合长度缓存到一个变量中,并在迭代中使用它。如果需要经常操作集合,建议把它拷贝到一个数组中。
- 如果可能的话,使用速度更快的API,比如querySelectorAll() 和 firstElementChild
- 注意 重绘和重排;批量修改样式时,“离线”操作DOM树,使用缓存,并减少访问布局信息的次数。
发生重排:
5.1 添加或删除可见的DOM元素。
5.2 元素位置改变
5.3 元素尺寸改变(包括:外边距,内边距 ,边框厚度、宽度、高度等属性改变)。
5.4 内容改变,例如:文本改变或图片被另一个不同尺寸的图片替代
5.5 页面渲染器初始化。
5.6 浏览器窗口尺寸改变。
5.7 根据改变的范围和程度,渲染树中或大或小的对应的部分也需要重新计算。有些改变会触发整个页面的重排:例如,当滚动条出现时。
减少重绘和重拍的次数
- 使元素脱离文档流。
- 对其应用多重改变。
3. 把元素带回文档中。该过程触发两次重排------第一步和第三部。
三种基本方法 使DOM脱离文档:
1. 隐藏元素 ,应用修改,重新显示
2. 使用文档片断(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
3. 将原始文档拷贝到一个脱离文档的节点中,修改副本,完成后在替换原始元素。
避免页面中的大部分重排:
1. 使用绝对位置定位页面上的动画元素,将其脱离文档流。
2. 让元素动起来,当它扩大时,会临时覆盖部分页面。但这只是页面一个小区域的重绘过程,不会产生重拍并重绘页面的大部分内容。
3. 当动画结束时恢复定位,从而只会下移一次文档的其他元素。
- 动画中使用绝对应为,使用拖放代理。
- 使用事件委托来减少事件处理器的数量。
第四章:算法和流程控制
- 避免使用for-in 循环,除非你需要遍历一个属性数量未知的对象,
- 改善循环性能的最佳方式是减少每次迭代运算量和减少循环迭代次数(达夫设备)
- 通常来说,swtich总是比if-else 快,但并不总是最佳解决方an
- 在判断条件较多时,使用查找表 比 if-else和switch更快。
- 浏览器的调用栈大小限制了递归算法在JavaScript中的应用;栈溢出错误会导致其他代码中断运行。
- 如果你遇到栈溢出错误,可将方法改为迭代算法,或使用Memoization来避免重复计算。
第五章 :字符串和正则表达式
- IE7及更早版本的链接字符串用数组项连接,新版浏览器中数组项连接是最慢的字符串连接方法之一。推荐使用简单的+和+=操作符替代,避免不必要的中间字符串。 正则不太理解 这里省略
第六章:快速相应的用户界面
- JS和用户界面更新在同一个进程中进行,因此一次只能处理一件事情。这意味着当javascript代码正在运行时,用户界面 不能相应输入,反之亦然,高效第管理UI线程就是要确保javascript不能运行太长事件,以免影响用户体验:
- 任何javascript任务都不应当执行超过100毫秒,过长的运行事件会导致UI更新出现明显的延迟,从而对用户体验产生负面影响。
- JavaScript运行期间,浏览器响应用户交互的行为存在差异。无论如何,javascript长时间运行将导致用户体验变得混乱和脱节。
- 定时器可用来安排代码延迟执行,他使得你可以把长时间运行脚本分解成一系列的小任务。
- web workers 是新版浏览器支持的特性,它允许你在UI线程外执行javascript代码从而避免锁定UI。
第七章:Ajax
高性能的Ajax包括以下方面:了解你项目的具体需求,选择正确的数据格式和与之匹配的传输技术。
- 减少请求数,可通过合并JS和CSS文件,或使用MXHR
- 缩短页面的加载时间,页面主要内容加载完成后,用Ajax获取那些次要的文件。
- 确保你的代码错误不会输出给用户,应在服务端处理错误。
- 知道何时使用成熟的Ajax类库,以及合适编写自己的底层Ajax代码
第八章:编程实践
- 通过避免使用eval() 和Function()构造器来避免双重求值带来的性能消耗。同样的,给setTimeout()和setInterval()传递函数而不是字符串作为参数
- 尽量使用直接量创建对象和数组。 直接量的创建和初始化都比非直接量形式要快。
- 避免做重复的工作。当需要检测浏览器时,可使用延迟加载或条件预加载。
- 在进行数学计算时。考虑使用直接操作数字的二进制形式的位运算
- JS的原生方法总会比你写的任何代码都要快。尽量使用原生方法。
第九章:构建并部署高性能JavaScript应用
- 合并javascript文件以减少HTTP请求数
- 压缩javascript 文件
- 在服务器端压缩javascript文件(Gzip编码)
- 通过正确设置 HTTP相应头来缓存javascript文件,通过向文件名增加时间戳来避免缓存问题。
- 使用CDN提供javascript文件;
第十章:工具
- 当网页或WEB应用变慢时,分析从网络下载的资源以及分析脚本的运行性能能让你zhuan注于那些需要优化的地方。
- 使用网络分析工具找出加载脚本和页面中其他资源的瓶颈,这会帮助你决定那些脚本需要延迟加载,或者需要进一步分析
- 把脚本尽可能的延迟加载可以加快页面渲染速度,给用户带来更好的体验
- 使用性能分析工具找出脚本运行过程中速度慢的地方,检查每个函数所消耗的时间,以及函数被调用的次数,通过调用栈自身提供的一些线索来找出需要集中精力优化的地方。
- 仔细观察函数的调用过程 你也许会发现其他需要优化目标
Javascript 是解释性语言,与编译性语言不同的是,它无需编译,而是将代码以字符串的形式交给javascript 引擎来执行,因此,代码性能在一定的程度上取决于客户端浏览器的javascript引擎。