----high performance javascript ----
1、不阻塞下载的script,可以加入defer标签,会延迟到dom加载完毕之后才执行
<script defer>
2、利用脚本动态创建的script标签(动态脚本注入),会在该元素添加到页面时启动下载,其下载和执行过程不会阻塞页面其他进程!
动态创建的script标签,最好放在<head>标签中,比放在body中保险(body中可能引发中止操作错误)
3、尽量把多个Js合并,减少http请求的时间。(动态脚本下载是异步的,所以大js也不会造成性能问题)
4、把页面加载需要的最小代码加到<body>标签的底部,在最后一个<script>中,可以动态加载script到head标签中。这样当最后一个script加载完毕时,所有的dom结构一定已经准备好,不需要通过window.onload事件来判断。
5、尽量减少引用全局变量,因为全局变量在整个作用域链的最底层。比如引用document.getElementById,可以把document存放在var doc中,再引用doc.getElementById,性能会好很多。
6、闭包函数会导致更多的内存开销,尤其IE中利用非原生js实现dom对象,闭包会导致内存泄漏;而且经常访问跨作用域的标识符,很容易带来性能损失
7、js面向对象的实现,通过构造函数和原型(__proto__)。当访问对象原型中的方法或属性,会导致遍历层次更深(搜索原型链),加剧性能问题
8、在浏览器中,dom与javascript的实现通常是分离的,通过接口进行通信,因此每次通过js调用dom都会有性能消耗。所以,尽量把运算、循环等放在js端,减少访问dom端的次数。
9、HTML集合(比如document.getElementsByName, document.links),一直与dom保持连接,当需要访问集合时,每次都要重复查询dom来获取最新的集合值,因此有很大的性能消耗。比如for( i=0; i<document.links.length; i++) 这个循环,每次比较数组长度时,都要再查询一次dom。所以提前把这些集合保存为局部变量,并利用另一个局部变量缓存集合的长度,再进行访问会好很多。
10、一般来说,遍历数组比遍历集合要快。但是将集合转化为数组的过程会带来消耗,因此需要权衡,如果集合本身很小,就不必转换为数组了。
11、查找元素,可以通过元素集合,也可以通过nextSibling。IE中,nextSibling要快很多,其它浏览器中二者速度差不多。
12、DOM元素属性比如 childNodes,不区分元素节点和其它节点(如textNode)。可以利用最新的document.children代替,这个属性返回数组,而非html集合,不仅过滤了非元素节点,而且遍历的性能也有极大提升
13、不要在布局信息修改的过程中,查询布局信息,这样会导致每次都要刷新渲染队列并重排。
14、如果要多次改变样式,可以合并多次改变,一次处理。比如cssText,或者直接修改css的class名称
15、批量修改dom,推荐使用文档片段(document fragment),在当前dom外构建一个子树,再拷贝回源dom文档
便利语法:使用fragment,比如var fragment = document.createFragment(); 当document.getElementById("id").appendChild(fragment)时,添加的是fragment的子节点,而不是片段本身
16、尽量减少布局信息的获取次数,获取后立即保存到局部变量。
17、动画使用绝对定位以及拖放代理
18、事件委托,减少事件处理器的数量(比如onclick)。冒泡机制可以使父元素捕获子元素的事件
19、优化条件语句的最佳方案:避免使用if-else或switch,改用查找表(数组)。这样当分支项显著增加时,性能也不会有额外开销。
20、字符串操作: str += 'one' + 'two' 比str += 'one' str+='two' 要慢。原因:内存中首先要创建临时字符串,并将"one" + "two"字符串赋值给它;之后与str相连接,结果再赋值给str,共四步。而第二种操作不需要产生临时字符串,直接连接,再赋值即可。(str = str +"one" + "two" 与第二种方案效果一致)
21、除IE外的浏览器,会将表达式中位于左侧的字符串分配更多的内存,所以将基础字符串(如str)放在最左端,就可以避免重复拷贝一个逐渐变大的基础字符串。
22、String.prototype.concat比+=要慢
23、绝大多数浏览器在js执行过程中都会阻止UI更新任务加入UI执行队列,因此保持一个js运行过程不超过100ms非常必要。
24、但当一个Js无法在100ms内完成任务时,可以让出UI线程的控制权,使用定时器setTimeout和setInterval可以做到。setTimeout(func, timeout)中,第一个参数表示要执行的函数,第二个参数表示过多久该任务被添加到UI执行队列。从setTimeout调用时开始计时。
创建一个定时器setTimeout会导致UI线程暂停,重置所有相关的浏览器限制,包括长时间运行脚本定时器,调用栈也会重置为0。所以可以利用定时器实现跨浏览器的长时间js脚本解决方案。
25、处理数组的过程中,如果满足以下两个条件:(1)处理过程不需要同步(2)数据处理不必按序;则可以异步处理数组,对数组的每一项,都利用setTimeout进行延迟,以保证ui的响应。延时时间至少25ms,再小的话可能不够一次UI更新。
一个很好的数组异步处理函数:processArray。(see High performace Javascript p119)
26、数据处理,xml很慢,冗余数据很多;json很快;html适合客户端瓶颈是cpu而非带宽的情况。当数据集很大,且对解析时间有要求时,推荐两种方案:
(1)json-p,使用动态脚本注入。可以跨域使用,但涉及敏感数据时不可用.
(json-p: 利用js可以跨域加载的方法,将json数据通过script的src标签连接到url中,将加载js的请求转发到跨域的服务器。只能用get方法,受url长度限制)
(2)字符分隔的自定义数据格式,使用XHR或动态脚本注入,用split解析。
27、最快的ajax请求就是没有请求╮(╯▽╰)╭所以,保证本地数据缓存是最简单的减少ajax的方法
(1)服务器端设置response的header,Expire头信息中设置缓存到期时间
(2)手动将响应文本保存到本地对象中,以url作为key进行索引。
28、加速AJAX的准则:
(1)减少请求数。可以合并js和css,或使用MXHR
(2)缩短页面加载时间。主要内容加载完成之后,用ajax加载次要文件
(3)确保代码错误不会输出给用户,在服务端处理错误
(4)知道何时使用成熟的ajax类库,何时自己编写底层ajax代码
29、用位操作比用算术运算快的多。如:表格奇偶行不同颜色,一般用row%2,其实用位运算,row&1==0为偶数行 ==1为奇数行 会快的多
num.toString(2)转换为二进制string输出
30、为避免每次都进行浏览器嗅探,可以使用延迟加载或者条件预加载,提前针对不同浏览器做好准备
延迟加载:首次执行时,针对不同浏览器用包含正确操作的新函数覆盖原始函数。首次执行有延迟,适合针对函数不会立即调用的情况使用
见高性能书p155
条件预加载:脚本加载完毕后,立即加载正确的函数,适合一个函数马上要被用到的情况
见高性能书p156
31、js的原生css选择器querySelector和querySelectorAll比基于js实现的css查询要快90%(H5特性)
----------------------jQuery--------------------------
1、要阻止事件冒泡,可以通过指定事件参数实现
$("element").bind(click, function(event) {
var txt = $("#msg").html() + "<p>hhh</p>";
$("#msg").html(txt);
event.stopPropagation();
});
2、阻止默认行为: event.preventDefault();
3、对于只需要触发一次,随后立即就要解除绑定的情况,可以使用one()方法,使用方法与bind类似
4、jQuery1.7中,使用on()方法替代bind()。可以利用on()方法绑定多个事件
$(document).ready(function() {
$("p").on({
mouseover: function() {
$(body).css("background-color", "lightgray");
}
mouseout: function() {
$(body).css("background-color", "lightblue");
}
click: function() {
$(body).css("background-color", "yellow");
}
});
});
5、通过代码触发事件,可以使用trigger,同时会触发默认事件。如果不想触发默认时间,可以使用triggerHandler
6、可以通过命名空间,把一个元素绑定的事件管理起来。这样删除事件时,只需指定命名空间即可。
$(document).ready(function() {
$("p").on({
mouseover.plugin: function() {
$(body).css("background-color", "lightgray");
}
mouseout.plugin: function() {
$(body).css("background-color", "lightblue");
}
click: function() {
$(body).css("background-color", "yellow");
}
});
});
$("p").off(".plugin") //解绑所有.plugin命名空间内的事件
7、CSS方法并不会加入动画队列中,因此
$("p").fadeOut("slow");
$("p").css("border", "5px solid blue");
并不能达到先淡出再改border的效果,而是在动画开始前已经改变了border样式。
解决方法:动画回调函数
$("p").fadeOut("slow", fuction() {
$("p").css("border", "5px solid blue");
});
8、如果一个动画没有执行完,用户即离开了动画产生的元素,下次回来时,动画还会继续从断点运行。可以利用stop方法停止原有动画。
jQuery stop() 方法用于停止动画或效果,在它们完成之前。
stop() 方法适用于所有 jQuery 效果函数,包括滑动、淡入淡出和自定义动画。
语法
$(selector).stop(stopAll,goToEnd);
可选的 stopAll 参数规定是否应该清除动画队列。默认是 false,即仅停止活动的动画,允许任何排入队列的动画向后执行。
可选的 goToEnd 参数规定是否立即完成当前动画。默认是 false。
因此,默认地,stop() 会清除在被选元素上指定的当前动画。
9、form中,$("form :input")表示表单中所有的表单对象,包括input, textarea, button, select等
$("form input")表示form下所有input标签
10、使用点连缀方法操作jquery对象时,如果中间发生了对象的改变,可以使用end()方法,将对象改为最初使用的对象。
$(this).addClass('selected')
.siblings().removeClass('selected')//到这里为止,操作对象改为$(this).siblings()
.end()//又将操作对象改为$(this)
.find(':radio').attr('checked', true);
11、jQuery通过ajax提交表单时,可以将参数序列化serialize()为字符串a=2&b=3
form中,只有具备name属性的标签,才会被序列化
12、可以通过serializeArray()方法序列化为json格式的对象
13、当ajax开始时,会触发jQuery全局事件,如ajaxStart, ajaxStop等方法。
如果不想触发全局事件,可以在使用ajax时,将参数中的global设置为false
$.ajax({
url: "test.html",
global: false
})
-----------------------------------high performace Web sites----------------------------------
1、 内联在页面中的内容不会被缓存。所以公司logo这种,可以作为背景放在外部css样式表中,可以缓存。
通过data: URL,可以在web页面中包含图片无需额外http请求
2、使用expire过期时间,要制定精确时间戳,且需要服务器和客户端的时钟严格同步。
可以使用cache-control: max-age=38232 指定组件被缓存多久,以秒为单位。(HTTP 1.1)
3、可以在http请求对组件进行压缩:Accept-Encoding: gzip, deflate
4、压缩只关心脚本、样式表、XML、JSON之类的文本响应。图片、pdf本身已经压缩了,不需要再压缩。
5、不管样式表需要什么时候出现,都一定要放在head标签中!因为浏览器为避免样式改变时重绘页面元素,会等待所有的样式表都加载完毕才构建呈现树,所以根本达不到逐步呈现页面的效果。所以即使一个元素用到的css样式表在呈现页面时并不需要,也不能放在页面底部。
6、HTTP 1.1规范建议浏览器从每个主机名并行下载两个组件。并行下载的数量可以通过各个浏览器的配置文件进行修改。
7、如果想增加并行下载的数量,可以使用CNAME(DNS别名)将组件放到多个主机名中。但会带来额外的性能开销。
8、下载脚本时,并行下载是被禁用的。也就是下载脚本时,其他的http请求会阻塞。
原因:(1)脚本可能使用document.write修改页面内容,需要等待确保页面能够恰当布局(2)保证脚本能够按照正确的顺序执行。
9、如果一个脚本可以defer(延迟执行),那么它一定可以放在页面的底部。
10、尽量避免使用css表达式:
width: expression(document.body.clientWidth < 600 ? "600px" : "auto");
因为不仅是页面发生变化时会触发计算,简单的页面滚动、甚至鼠标滑过页面都会引发很多次计算。
如果一定要计算一次,那么可以在本次执行中重写自身,将原有表达式覆盖。
11、DNS查找的数量与页面中唯一主机地址相等。因此唯一主机地址越少,dns查找次数越少,消耗的时间越少。
但主机地址少会导致并行下载的数量也会降低。因此建议将一个页面的所有组件分别放置在2-4个唯一地址主机名下,可以实现较好的权衡。
12、如果要使用重定向,可以在js中使用document.location设置url;可以通过HTML文档头的meta refresh标签指定秒数之后重定向到url;可以通过http状态吗
13、可以使用http etag对缓存进行资源级别的有效标识。expire指定了资源过期时间,在过期时间之前,不会产生http请求。过期之后,产生http请求中如果包含了etag,服务器端比较etag如果相同,说明是同一资源无需更新,则返回304表明无需更新,使用缓存即可。