24小时咨询热线: 18240091316 集团首页

JavaScript性能优化小知识总结(二)

作者: 来源: 时间: 2017-05-05 16:04:40 字体: [大] [中] [小]

前言

一直在学习javascript,也有看过《犀利开发Jquery内核详解与实践》,对这本书的评价只有两个字犀利,可能是对javascript理解的还不够透彻异或是自己太笨,更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。

鉴于想让自己有一个提升,进不了一个更加广阔的天地,总得找一个属于自己的居所好好生存,所以平时会有意无意的去积累一些使用jQuerry的常用知识,特别是对于性能要求这一块,总是会想是不是有更好的方式来实现。

下面是我总结的一些小技巧,仅供参考。(我先会说一个总标题,然后用一小段话来说明这个意思 再最后用一个demo来简单言明)


使用firstChild和nextSibling代替childNodes遍历dom元素

var nodes = element.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
   var node = nodes[i];
   //……
}
//可以替换为:
var node = element.firstChild;
while (node) {
    //……
  node = node.nextSibling;

删除DOM节点

删除dom节点之前,一定要删除注册在该节点上的事件,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。另外,在removeChild和innerHTML=’’二者之间,尽量选择后者。因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点

使用事件代理

任何可以冒泡的事件都不仅仅可以在事件目标上进行处理,目标的任何祖先节点上也能处理,使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理,同样,对于内容动态增加并且子节点都需要相同的事件处理函数的情况,可以把事件注册提到父节点上,这样就不需要为每个子节点注册事件监听了。另外,现有的js库都采用observe方式来创建事件监听,其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听

重复使用的调用结果,事先保存到局部变量

 //避免多次取值的调用开销
 var h1 = element1.clientHeight + num1;
 var h2 = element1.clientHeight + num2;
 //可以替换为:
 var eleHeight = element1.clientHeight;
 var h1 = eleHeight + num1;
 var h2 = eleHeight + num2;

注意NodeList

最小化访问NodeList的次数可以极大的改进脚本的性能


var images = document.getElementsByTagName('img');
for (var i = 0, len = images.length; i < len; i++) {

}

编写JavaScript的时候一定要知道何时返回NodeList对象,这样可以最小化对它们的访问

  • 进行了对getElementsByTagName()的调用

  • 获取了元素的childNodes属性

  • 获取了元素的attributes属性

  • 访问了特殊的集合,如document.forms、document.images等等

要了解了当使用NodeList对象时,合理使用会极大的提升代码执行速度

优化循环

可以使用下面几种方式来优化循环

  • 减值迭代

大多数循环使用一个从0开始、增加到某个特定值的迭代器,在很多情况下,从最大值开始,在循环中不断减值的迭代器更加高效

  • 简化终止条件

由于每次循环过程都会计算终止条件,所以必须保证它尽可能快,也就是说避免属性查找或者其它的操作,最好是将循环控制量保存到局部变量中,也就是说对数组或列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。

var list = document.getElementsByTagName('p');
for (var i = 0; i < list.length; i++) {
   //……
}

//替换为:
var list = document.getElementsByTagName('p');
for (var i = 0, l = list.length; i < l; i++) {
   //……
}
  • 简化循环体

循环体是执行最多的,所以要确保其被最大限度的优化

  • 使用后测试循环

在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以,就应该尽量少用。for(;;)和while循环,while循环的效率要优于for(;;),可能是因为for(;;)结构的问题,需要经常跳转回去。

var arr = [1, 2, 3, 4, 5, 6, 7];
var sum = 0;
for (var i = 0, l = arr.length; i < l; i++) {
   sum += arr[i];
}

//可以考虑替换为:

var arr = [1, 2, 3, 4, 5, 6, 7];
var sum = 0, l = arr.length;
while (l--) {
    sum += arr[l];
}

最常用的for循环和while循环都是前测试循环,而如do-while这种后测试循环,可以避免最初终止条件的计算,因此运行更快。

展开循环

当循环次数是确定的,消除循环并使用多次函数调用往往会更快。

避免双重解释

如果要提高代码性能,尽可能避免出现需要按照JavaScript解释的字符串,也就是

  • 尽量少使用eval函数

使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间,而且使用Eval带来的安全性问题也是不容忽视的。

  • 不要使用Function构造器

不要给setTimeout或者setInterval传递字符串参数

var num = 0;
setTimeout('num++', 10);
//可以替换为:
var num = 0;
function addNum() {
    num++;
}
setTimeout(addNum, 10);

缩短否定检测

if (oTest != '#ff0000') {
    //do something
}
if (oTest != null) {
    //do something
}
if (oTest != false) {
    //do something
}
//虽然这些都正确,但用逻辑非操作符来操作也有同样的效果:
if (!oTest) {
    //do something
}

条件分支

  • 将条件分支,按可能性顺序从高到低排列:可以减少解释器对条件的探测次数

  • 在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。

  • 使用三目运算符替代条件分支

if (a > b) {
     num = a;
} else {
     num = b;
}
//可以替换为:
num = a > b ? a : b;

使用常量

  • 重复值:任何在多处用到的值都应该抽取为一个常量

  • 用户界面字符串:任何用于显示给用户的字符串,都应该抽取出来以方便国际化

  • URLs:在Web应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有的URL

  • 任意可能会更改的值:每当你用到字面量值的时候,你都要问一下自己这个值在未来是不是会变化,如果答案是“是”,那么这个值就应该被提取出来作为一个常量。


永远不要忽略代码优化工作,重构是一项从项目开始到结束需要持续的工作,只有不断的优化代码才能让代码的执行效率越来越好。