高性能网站建设进阶指南(三)
第五章 整合异步脚本
1、脚本如果按常规方式加载<scripg src=""></script不仅会阻塞页面中其它内容的下载,还会阻塞脚本后面所有元素的渲染,异步加载脚本可以避免这种阻塞情况。但是代码异步执行时可能会出现竞争状态而导致出现未定义标识符的错误。如果行内脚本依赖外部脚本(如引用jquery),保证执行顺序就很重要,外部脚本必须在行内脚本之前下载、解析、执行。
2、竞争状态
没有一种技术既能并行下载又能保持执行顺序,唯一的特例是firefox中执行的Script DOM Element
3、异步加载脚本时保持执行顺序
当外部脚本按常规加载时,它会阻塞行内代码的执行,竞争状态不会发生,如果我们开始异步加载脚本,就需要把行内代码和依赖行内代码的外部脚本整合起来,这些整合技术是:
(1)硬编码回调(HardCoded CallBack):让外部代码调用行内代码里的函数,这种方法不灵活。
(2)window Onload:通过监听window的Onload事件来触发行内代码的执行,有两个缺点:一是异步脚本是通过阻塞onload事件的方式加载的;二是可能导致行内代码延迟执行。
(3)定时器(Timer):使用轮询方法保证在行内代码执行之前所依赖的外部脚本已经加载,如setTimeOut()方法。
(4)Script Onload:是最好的选择,通过监听脚本的Onload事件而解决了所有问题。
(5)Degrading Script Tags--降级使用script标签,避免了未定义标识符的错误,如下代码:
<script src="jquery.js" type="text/javascript">
jquery('p').addClass("pretty");
</script>
但这种语法有的浏览器不支持
4、之前都说的是加载单个脚本,如果有多个脚本和行内代码要按指定的顺序执行。
第六章 布置行内脚本
行内脚本不会产生额外的http请求,但会阻塞页面上的资源并下载,还会阻塞逐步渲染。
解决方案是:
(1)把行内脚本移到最底部
(2)使用异步回调启动JS的执行----简单的异步调用技术就是使用setTimeout。
function longCode(){
var vstart=Number(new Date());
while((vstart+5000)>Number(new Date()))
{};
setTimeout(vstart,0);
}
(3)使用script的defer属性,没有setTimeout()好。
(4)保持CSS和JS的执行顺序
浏览器是按照样式表的在页面中列出的顺序应用它们的,而与下载顺序无关。
(5)把行内脚本放在样式表之后有风险:当样式表下载完成并且行内脚本执行完成时,后续资源才开始下载,大部分下载都不阻塞行内脚本。
解决方案:调整行内脚本的位置,使其不出现在样式表和任何其它资源之间。即把行内脚本放在样式表之前。
第七章 编写高效的JS
1、管理作用域
当执行JS时,JS会创建一个执行上下文,执行上下文设定了代码执行时所处的环境,JS引擎会在页面加载后创建一个全局的执行上下文,然后每执行一个函数时都会创建一个对应的执行上下文,最终建立一个执行上下文的堆栈,当前起作用的执行上下文在堆栈的最顶部。
每个执行上下文都有一个与之关联的作用域链,用于解析标识符,作用域链包含一个或多个变量对象,这些对象定义了执行上下文作用域内的标识符。全局执行上下文的作用域链中只有一个变量对象,它定义了JSk上所有可用的变量和函数。
当函数被创建时,JS引擎会把创建时执行上下文的作用域赋给函数的内部属性,当函数被执行时,JS引擎会创建一个活动对象,并在初始化时给this,arguments、命名参数和该函数的所有全局变量赋值。
活动对象会出现在执行上下文作用域链的顶端,紧接其后的是函数属性中的对象。
下面这个代码执行时执行上下文和作用域链的关系:
function Add(num1,num2){
return num1+num2;
}
var result=Add(5,10);
console.log(result);
局部变量是JS中读写最快的标识符,标识符越深查找越慢,这种现象几乎在所有的浏览器中都存在。标识符在作用域链中的位置越靠上,存取的速度就越快。
任何非局部变量在函数中使用超过了一次,则应该把它存储为局部变量,如这段代码:
function CreatForChild(elementId)
{
var elementR=document.getElementById(elementId);
newElement=document.createElement('div');
elementR.appendChild(newElement);
}
因为document的使用超过了1次,存储为局部变量:
var doc=document;
全局变量对象始终是作用域中的最后一个对象,所以对全局标识符的解析总是最慢的。
注意:给变量首次赋值时忽略var关键字,会带来性能问题,因为当对未声明的变量赋值时,JS引擎会自动将其创建成一个全局变量。
在代码执行过程中,执行上下文对应的作用域链通常保持不变,但有两个语句会临时增长执行上下文的作用域链,是with语句和try...catch语句中的catch从句,但是catch从句的影响要比with语句的影响要小,但是要注意不要在catch从句中执行过多的代码,以免对性能造成影响。
personInfo();
function personInfo(){
var person={name:'Marry',age:30};
with(person){
document.write(name+" is:"+age+"岁");
}
}
WITH语句加深作用域链
管理好作用域链是一种提高性能的简易方法。
2、高效的数据存取
在脚本中有四个地方可以存取数据:
(1)字面量值
(2)变量
(3)数组元素:通过索引
(4)对象属性:通过属性值
从字面量值和局部变量中存取数据的开销差异很小,不需要担心性能问题。
各个浏览器存取数据的时间:
function process(data)
{
if(data.count>0)
{
for(var i=0;i<data.count;i++)
{
processData(data.items[i]);
}
}
}
改写成局部变量存取时间将会缩短
function process(data)
{
var count=data.count;
if(count>0)
{
for(var i=0;i<count;i++)
{
processData(data.items[i]);
}
}
}
在数据存取时,超过一次的变量属性和数据元素存储为局部变量是提升性能的一种好方法。
对于在多数浏览器来说,取变量属性用data.count或者data[count]几乎没有区别。
window.onload=function(){
var divs=document.getElementsByTagName(div);
for(var i=0;i<divs.length;i++)
{
var div=divs[i];
process(div);//输出DIV里面的内容
}
}
改写成这样存取速度更快
window.onload=function(){
var divs=document.getElementsByTagName("div");
for(var i=0;lens=divs.length;i<lens;i++)
{
var div=divs[i];
process(div);//输出DIV里面的内容
}
}
3、流控制
(1)快速条件判断