如何改进javascript代码的性能


Posted in Javascript onApril 02, 2015

本来在那片编写可维护性代码文章后就要总结这篇代码性能文章的,耽搁了几天,本来也是决定每天都要更新一篇文章的,因为以前欠下太多东西没总结,学过的东西没去总结真的很快就忘记了,记录一下在你脑力留下更深的印象,特别是这些可维护性代码,性能什么的,当在你脑子里形成一种习惯了,那你就牛了!这里也要给初学者一个建议:多总结你学过的东西,因为这其实也是在学习新知识! 好,进入我们的主题:如何提高JS代码的性能。

1.优化DOM交互

DOM与我们的页面紧密相关,浏览器渲染页面也就是在渲染解析后的DOM元素,DOM操作与交互要消耗大量的时间,因为它们往往需要重新渲染整个页面或者一部分。进一步说,看似细微的一些操作也可能需要花很多时间来执行,因为DOM要处理的信息非常多,因此我们应该尽可能地优化与DOM相关的操作,加快浏览器对页面的渲染!为什么有些DOM操作会影响页面性能,可以查看我写的一些关于浏览器原理的文章:

ok,优化DOM操作,我们主要有一些几种方式:

1.1 最小化现场更新

什么是DOM的现场更新:需要对DOM部分已经显示的页面的一部分的显示立即更新。但是,每一个更改,不管是插入单个字符,还是一处整个片段,都有一定的性能惩罚,因为浏览器需要重新计算无数尺寸以进行更新(相关知识请阅读:)。所以,现场更新进行的越多,代码执行所花的时间就越长,反之代码执行越快,如下:

var list = document.getElementById('mylist'),
      item,
      i;
for(i = 0; i < 10; i++){
  item = document.creatElement('li');
  list.appendChild(item);
  item.appendChild(document.creatTextNode('item' + i));
}

这段代码为列表mylist添加了10个项目,没添加一个项目都要进行2次的现场更新:添加元素和添加文本节点,所以这个操作一个需要完成20个现场更新,每个更新都会损失性能,可见这样的代码运行起来是相对缓慢的。

解决的方法是使用文档碎片间接地更改DOM元素:

var list = document.getElementById('mylist'),
      fragment = document.creatDocumentFragment(),
      item,
      i;
for(i = 0; i < 10; i++){
  item = document.creatElement('li');
  fragment .appendChild(item);
  item.appendChild(document.creatTextNode('item' + i));
}
list.appendChild(fragment);

像这样的代码只需进行一次的现场更新。记住,当给appendChild()传入文档碎片是,只有文档碎片中的子节点才会被添加到目标元素,碎片本身不会被添加。

现在,你应该明白你用循环直接进行DOM节点的增删查改是多么对不起浏览器的事了吧 `(∩_∩)′ 。

1.2 使用 innerHTML

除了上面代码中使用的creatElement() 和 appendChild()结合的方法创建DOM元素之外,还有通过给innerHTML赋值来创建。对于小的DOM更改而言,两种方法的效率其实差不多,但对于大量的DOM节点的更改,后者要比前者快得多!为啥捏?

因为当我们给innerHTML赋值时,后台会创建一个HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于Javascript的DOM调用,由于内部方法是编译好的而非解释执行的,所以执行代码的速度要快很多!

用innerHTML改写上面的例子:

var list = document.getElementById('mylist'),
       html = '', //声明一个空字符串
        i;
  for(i = 0; i < 10; i++){
    html += '<li>item' + i + '</li>';
  }
  list.innerHTML = html; // 这里记得innerHTML后面的HTML四个字母都要大写!

这种方式同样也只进行了一次的现场更新,并且性能要比上一种方式要好!虽然在字符串的链接上有点性能损失。

1.3 使用事件代理/事件委托

事件处理程序为web应用提供交互能力,因此许多开发人员会不分青红皂白地向页面中添加大量的处理程序,有个问题就是一个页面上的事件处理程序数量将直接关系到页面的整体运行性能。为什么捏?

首先,事件处理程序对应至少一个函数,JS中每个函数都是对象,都会占用内存,内存中的对象越多,性能就越差。

其次,我们必须事先指定所有事件处理程序,这就导致了DOM访问次数增多,会延迟整个页面的交互就绪时间,页面响应用户操作变得相对缓慢。

所以减少事件处理程序同样也可以让我们的页面更牛畅!使用事件委托势在必得啊!

事件委托的原理其实就是事件冒泡,只指定一个事件处理程序就可以管理某一类型操作的所有事件。例如:click事件会一直冒泡到document层次,也就是说我们不必为每个元素添加事件,只需在较高的层次的元素上添加事件处理程序即可,然后利用事件对象(event)的属性或方法去判断当前点击的元素,然后做出相应的响应。这个我就不展开讲了,初学者可以自行查阅事件冒泡知识。

2.作用域很重要

说到作用域啊就很容易想到作用域链(scope chain),我们知道要搜索一个变量,所在的执行环境都要沿着这条作用域向上搜索这个变量,作用域链上有很多的变量,那么我们就得遍历,遍历就需要时间啊,而且你越往上查找所需时间越多,如果我们能减少这个时间,我们代码执行效率不是可以提高了吗?

好聪明啊,ok,我看看有哪些方式可以减少这个时间:

2.1 避免全局查找

这是性能优化的一重点,上面也说了,越往上查找时间越多,也就是说查找全局变量和函数比局部要多!看代码:

function updateUI(){
  var imgs = document.getElementByTagName('img');
  for(var i = 0 ,lng = imgs.length;i < lng;i ++){
    imgss[i].title = document.title + 'image' + i;
  }
  var msg = docuement.getElementById('msg');
  msg.innerHTML = 'update complete.';
}

这代码很正常呀!我之前也经常这么做滴。但是我们细心可以发现,这段代码有三处引用了全局变量document,如果我们的页面很多图片,那么在for循环中的document就会被执行上百次,而每次都要需要在作用域链中查找,时间都去哪了,我还没......停!。

我们可以通过在函数中创建一个局部变量保存对document的引用,这样,我们在函数里任何地方引用document都不用跑到全局变量去找了。这样就改进了代码的性能,看代码:

function updateUI(){
  var doc = document; // 将document保存在局部变量doc中
  var imgs = doc.getElementByTagName('img');
  for(var i = 0 ,lng = imgs.length;i < lng;i ++){
    imgss[i].title = doc.title + 'image' + i;
  }
  var msg = doc.getElementById('msg');
  msg.innerHTML = 'update complete.';
}

所以啊,我们在开发中,如果在函数中会经常用到全局变量,把它保存在局部变量中!

2.2 避免使用with语句

用with语句延长了作用域,查找变量同样费时间,这个我们一般不会用到,所以不展开了。解决方法还是和上面的例子一样,将全局变量保存在局部变量中!

3.优化循环

循环在编程中可谓家常便饭,在js中也随处可见,循环体会反复地执行同一段代码,执行时间一直累加,所以能够对循环体的代码进行优化也可以大大减少执行时间!如何优化?四种方式。

3.1 减值迭代

我们写迭代器(循环条件)的时候一般都这样(var i = 0;i < 10;i ++),从0开始,增加到某个特定值。然而在很多情况下,如果在循环中使用减值迭代器效率更高。我测试了下,如果循环体不复杂的话,两者差不多!

//增值迭代 --效率较低
for(var i = 0;i < items.length;i++){
  doSomething(items[i]); 
}
//减值迭代 --效率较高
for(var i = items.length - 1;i >= 0;i--){
  doSomething(items[i]); 
}

3.2 简化终止条件

由于每次循环都会计算终止条件,所以必须保证它的执行尽可能地块。这里主要是避免其他DOM元素及其属性的的查找。

//看终止条件,每次循环都需要查询items及其length属性
for(var i = 0;i < items.length;i++){
  doSomething(items[i]); 
}

//将items.length的值保存在局部变量lng中。
for(var i = 0,lng = items.length;i < lng;i++){
  doSomething(items[i]); 
}

3.3 简化循环体

原因和上面以上的,所以在循环体内避免大量的密集的操作。

这其实和上面讲的:1.1 最小化现场更新 。是一样的优化方式。可以倒回去看看。

4.基本的算法优化

在计算机中,算法的复杂度用O表示。下面是javascript中几种常见的算法类型:

O(1) :常数,不管有多少值,执行的时间都是恒定的,比如简单值和存储在变量中的值。
O(log n):对数,总的执行时间和数量有关,但不一定要获取每一个值,如:二分法查找
O(n) :线性,总执行时间和数量直接相关,如:遍历
O(n*n) :平方,总执行时间和数量有关,每个值至少获取N次,如:插入排序
ok,有了上面的知识,我们就可以对javascript进行一些算法上的优化了。看代码:

var value = 5;
var sum = value + 10;
alert(sum);

这段代码进行了4次常量值的查找:数字5,变量value,数字10,变量sum,这段代码的算法复杂度就是O(1)。又如:

var value = [10,5];
var sum = value[0] + value[1];
alert(sum);

在javascript中访问数组元素也是一个O(1)操作,和简单的变量查找效率一样。再看:

var value = {one:10,two:10};
var sum = value.one + value.two;
alert(sum);

要表达的是访问对象上的属性要比访问数组和变量的效率低。因为这是一个O(n)操作。你需要在对象的原型链中查找该属性,所花时间较多。

好了,看完这个是不是感觉眼前一片光明啊。其实我们前面所讲的要把经常用到的全局属性保存在一个局部变量中就是根据这个原理了,访问全局属性是一个O(n)的操作,而访问变量是一个O(1)的操作,大声告诉我,挖掘机哪家强啊!

5.最小化语句数

前面讲的优化差不多都是和精简优化语句有关的,是的,我觉得代码的质量和数量就是性能的评判标准。前面讲了一些代码质量相关的优化,这里就讲讲代码数量的优化。

5.1 精简变量声明

//用了5条语句声明5个变量
var count = 5;
var color = 'red';
var values = [1,2,3];
var now = new Date();

//用了1条语句声明5个变量,注意每个变量用逗号隔开
var count = 5,
  color = 'red',
  values = [1,2,3],
  now = new Date();

5.2 使用数组和对象字面量

// 创建两个对象 ----不好的方式
//one 四条语句
var values = new Array();
values[0] = 123;
values[1] = 456;
values[2] = 789;
//two 四条语句
var person = new Object();
person.name = 'jozo';
person.age = 21;
person.sayName = function(){
  alert(this.name);
};
// 创建两个对象 ----推荐的方式
//one 1条语句
var values = [123,456,789]
//two 1条语句
var person = {
  name : 'jozo',
  age : 21,
  sayName : function(){
  alert(this.name);
};

6.其他

写累了,如有不正确的地方请指正哦,还有一些其他的优化,下次文章继续!

Javascript 相关文章推荐
基于jquery的tab切换 js原理
Apr 01 Javascript
extjs grid设置某列背景颜色和字体颜色的方法
Sep 03 Javascript
jquery+json实现数据列表分页示例代码
Nov 15 Javascript
javascript正则表达式使用replace()替换手机号的方法
Jan 19 Javascript
Node.js抓取中文网页乱码问题和解决方法
Feb 10 Javascript
js进行表单验证实例分析
Feb 10 Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
Dec 14 Javascript
Linux系统中利用node.js提取Word(doc/docx)及PDF文本的内容
Jun 17 Javascript
javascript 中模板方法单例的实现方法
Oct 17 Javascript
细说webpack6 Babel的使用详解
Sep 26 Javascript
vue+iview框架实现左侧动态菜单功能的示例代码
Jul 23 Javascript
Vue自定义表单内容检查rules实例
Oct 30 Javascript
JavaScript获得表单target属性的方法
Apr 02 #Javascript
AngularJS快速入门
Apr 02 #Javascript
Javascript中的包装类型介绍
Apr 02 #Javascript
Javascript中使用parseInt函数需要注意的问题
Apr 02 #Javascript
Yii2使用Bootbox插件实现自定义弹窗
Apr 02 #Javascript
jquery制作多功能轮播图插件
Apr 02 #Javascript
Javascript中3个需要注意的运算符
Apr 02 #Javascript
You might like
php将fileterms函数返回的结果变成可读的形式
2011/04/21 PHP
查找mysql字段中固定字符串并替换的几个方法
2012/09/23 PHP
PHP的Laravel框架中使用AdminLTE模板来编写网站后台界面
2016/03/21 PHP
php + nginx项目中的权限详解
2017/05/23 PHP
Laravel5框架添加自定义辅助函数的方法
2018/08/01 PHP
从数据结构的角度分析 for each in 比 for in 快的多
2013/07/07 Javascript
三种方式获取XMLHttpRequest对象
2014/04/21 Javascript
JS数组array元素的添加和删除方法代码实例
2015/06/01 Javascript
Javascript实现的SHA-256加密算法完整实例
2016/02/02 Javascript
javascript 广告移动特效的实现代码
2016/06/25 Javascript
JS中使用mailto实现将用户在网页中输入的内容传递到本地邮件客户端
2016/10/08 Javascript
原生js实现放大镜效果
2017/01/11 Javascript
从零学习node.js之详解异步控制工具async(八)
2017/02/27 Javascript
详解Node.js串行化流程控制
2017/05/04 Javascript
jQuery手风琴的简单制作
2017/05/12 jQuery
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
koa2实现登录注册功能的示例代码
2018/12/03 Javascript
微信小程序的线程架构【推荐】
2019/05/14 Javascript
JS实现动态星空背景效果
2019/11/01 Javascript
node koa2 ssr项目搭建的方法步骤
2020/12/11 Javascript
有关wxpython pyqt内存占用问题分析
2014/06/09 Python
python字典多条件排序方法实例
2014/06/30 Python
python持久性管理pickle模块详细介绍
2015/02/18 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
python脚本调用iftop 统计业务应用流量的思路详解
2019/10/11 Python
python分布式编程实现过程解析
2019/11/08 Python
python request 模块详细介绍
2020/11/10 Python
澳大利亚床上用品、浴巾和家居用品购物网站:Bambury
2020/04/16 全球购物
J2EE的优越性主要表现在哪些方面
2016/03/28 面试题
大学生找工作推荐信范文
2013/11/28 职场文书
公司优秀员工获奖感言
2014/08/14 职场文书
教师四风问题整改措施
2014/09/25 职场文书
学校感恩节活动策划方案
2014/10/06 职场文书
流动人口婚育证明
2014/10/19 职场文书
2015年教师自我评价范文
2015/03/04 职场文书
2015年中秋晚会主持稿
2015/07/30 职场文书