做有态度的前端团队

网易FEG前端团队

学习笔记:编写高效的javascript(老司机请绕道)

偶尔写得很挫的代码会被鄙视,甚至会影响到用户的每一次操作,所以开始思考如何让自己码下的代码能在所有浏览器中最高效地执行。


下面是我最近在读的一本名叫《高性能网站建设进阶指南》的书的一些笔记,做下简单的分享。

一、学会管理作用域

当执行JavaScript代码时,JavaScript引擎会创建一个执行上下文,每个执行上下文都有一个与之关联的作用域链。JavaScript引擎会在页面加载后创建一个全局的执行上下文,然后每执行一个函数都会创建一个对应的执行上下文,同时会创建一个活动对象,并在初始化时给this、arguments、命名参数和该函数的所有局部变量赋值。


JavaScript就是通过搜索这个作用域链来解析诸如变量和函数名这样的标识符。查找从作用域第一个对象开始,找不到了就继续找下一个,找到就立马结束。

   
   function add(num1, num2){
       return num1 + num2;
   }
   
   var result = add(1+2);

对应的作用域链关系图
feg.jpg

就像上面的代码,num1和num2就存在于当前的活动对象中,而不用到全局对象中查找。而且,标识符在作用域中的位置越深,查找和访问它所需时间就越长,所以,如果作用域管理不当对脚本执行的时间会带来一定的负面影响。

1) 使用局部变量

局部变量是js读写中最快的标识符。

   
   function createChildFor(id){
       var ele = document.getElementById(id),
           new_ele = document.createElement("div");
       ele.appendChild(new_ele);         
   }

这里两次引用了全局变量document。为了更快的引用,应该把它存到一个变量中,如:

   
   function createChildFor(id){
       var doc = document, //存到一个局部变量中,这样就解析它比解析document更快
           ele = doc.getElementById(id),
           new_ele = doc.createElement("div");
       ele.appendChild(new_ele);         
   }

2) 避免增长作用域链

代码执行过程中,作用域链通常保持不变的。但是会有两个语句会临时增长作用域链。比如with语句和try-catch语句中的catch语句。

从以上两点看来,管理好作用域链的深度,是一种只要少量工作就能提高性能的简易方法,值得我们平时注意与学习。

二、高效的数据存取

脚本一般有4种地方可以存取数据:字面量值、变量、数组元素、对象属性。前两者总是最快的,而存取数组元素和对象属性会引起性能损耗,所以如果数组元素和对象属性的使用超过一次,为了提高存取速度,就应该把他存为局部变量。

   
   var divs = document.getElementByTagName("div");
   for(var i = 0; i < div.length; i++){//避免
       var div = div[i];
       process(div);
   }

上面每次读取div.length或divs[i]都回对页面查询一次,而且操作DOM对象的开销总是比非DOM的要大..尽可能把这类值存到局部变量中,这也就是我们平时为什么要先用局部变量保存数组长度的原因。

   
   var divs = document.getElementByTagName("div");
   for(var i = 0, len = divs.length; i < len; i++){//better
       var div = div[i];
       process(div);
   }

三、流控制

1) 快速条件判断

a) if语句

两个之内的离散值需要判断
大量的值能容易分到不同的区间范围

b) switch语句

超过两个而少于10个离散值需要判断(过多的if改switch,简化结构,提示性能)
条件值非线性的,无法分离出区间范围

c) 另外一种选择:数组查询

超过10个值需要判断
条件对应结果是单一值,而不是一系列操作

   //把所有结果存在数组中,通过数组索引映射value变量
   var result = [result0,result1,result2,result3,result4,result5,result6,result7];
   //返回
   return result[value];

如果查询范围很大时,使用数组查询就变得比条件判断要快一些。

2) 快速循环

a) 循环性能提升,用局部变量保存数组长度,将循环递减到0。

var values = [1,2,3,4,5];
var len = values.length;

//for循环
for(var i = len; i--;){ //将循环递减到0
   process(values[i]);
}

//do-while循环
var j = len;
do{
   process(values[--j]);
}while(j)

//while循环
var k = len;
while(k--){
   process(values[k]);
}

b) 避免for-in循环,for-in比其他循环慢,若明确知道所有的属性名还是建议用其他的循环。
c) 展开循环,适用于大数组,可以考虑使用Duff策略提高执行速度。

四、避免运行时间过长的脚本

过多的DOM交互
过多的循环
过多的递归

手机阅读请扫描下方二维码:

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif