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. 工具(略,很多工具偏老,不推荐也不说了)

参考他人的总结