当前位置: 移动技术网 > IT编程>开发语言>JavaScript > JS性能优化实现方法及优点进行

JS性能优化实现方法及优点进行

2020年08月31日  | 移动技术网IT编程  | 我要评论
最近刚阅读完《高性能javascript》,想谈谈对js性能优化的看法。理解有些不同,可能还需要各位多多提醒。话不多说,提到javascript难免会联想到文档对象模型(dom),它作用于xml和ht

最近刚阅读完《高性能javascript》,想谈谈对js性能优化的看法。理解有些不同,可能还需要各位多多提醒。

话不多说,提到javascript难免会联想到文档对象模型(dom),它作用于xml和html文档的程序接口(api),位于浏览器中,主要用来与html文档打交道。同样也用于web程序中获取xml文档,并使用dom api来访问文档中的数据。尽管dom是个与语言无关的api,它在浏览器中的接口却是用javascript实现的。客户端脚本编程大多数时候是在和底层文档(underlying document) 打交道,dom就成为现在javascript编程中的重要部分。

浏览器通常会把dom和js独立实现。比如在ie中,javascript的实现名为jscript,位于jscript.dll文件中;dom的实现则存在另一个库中,名为mshtml.dll(内部称为trident)。这个分离允许的其他技术和语言,比如vbscript,能共享使用dom以及trident提供的渲染函数。safari中的dom和渲染使用的webkit中的webcode实现,javascript部分是由独立的javascriptcode引擎(最新版本的名字为squirrelfish)来实现。google chrome同样使用webkit中的webcore库来渲染页面,但javascript引擎是他们自己研发的,名为v8。firefox的javascript引擎名为spidermonkey(最新版的名字为tracemonkey),与名为gecko的渲染引擎相互独立。

把dom和javascript(这里指ecmascript,javascript使用的ecmascript版本为ecmascript-262)各自想象一个岛屿,它们之间用收费桥梁连接。ecmascript每次访问dom,都需要途经这座桥,并交纳“过桥费”。访问dom的次数越多,费用越高。所以想办法减少过桥次数就可以减少费用。

一、超载运输

上面提到“过桥费”很贵啊,那么我们尽量使需要多次去访问dom的时候全部整合到一次。比如最简单的例子:

function innerhtmlloop(){
   for(var count = 0;count < 15000 ;count++){
      document.getelementbyid('here').innerhtml +='a';
  } 
}

这个函数循环修改页面元素的内容。这段代码存在循环迭代,该元素都被访问两次,一次是读取innerhtml属性值,另一次是重写它。(意味着每次循环都必须付“过桥费”)。

为了减少费用,我们采取一种更高效的方法,例:

function innerhtmlloop2(){
   var content = ' ';
   for(var count = 0;count < 15000 ;count++){
      content +='a';
  } 
  document.getelementbyid('here').innerhtml +=content;
}

这样只需要付一次“过桥费”,就可以完成相同的功能。运行速度在不同的浏览器中都有大幅度的提升,例如ie6中,使用innerhtmlloop2()比使用innerhtmlloop()快155倍。(所以现实当中好多大卡车超载也是为了省这个费用,一次性多赚点。不过还是量力而行。程序也是一样,均衡存乎万物之间。)

二、触手可及

尽管使用优化过的javascript引擎的新型浏览器,对于对象成员引用也存在一些性能问题。对象在原型链中存在的未知越深,找到它也就越慢,例如不太常见的写法:window.location.href。每次遇到点操作符,嵌套成员会导致javascipt引擎搜索所有对象成员。对象成员嵌套得越深,读取速度就会越慢。执行location.href总是比window.location.href要快,后者也比window.location.href.tostring()要快。如果这些属性不是对象的实例属性,那么成员解析还需要搜索原型链,这会花更多的时间。

由于类似的性能问题都是与对象成员有关,因此应该尽可能避免使用它们。更准确地说,应当注意,只在必要时使用对象成员。例如,在同一个函数中没有必要多次读取同一个对象成员。例:

function haseitherclass(element,classname1,classname2){
   return element.classname == classname1 || element.classname == classname2; 
}

以上的代码,element.classname读取了2次。意味着在该函数语句中2次成员查找都是通过读取属性值,那么有必要子啊第一次查找到值后就将其存储在局部变量中,因为局部变量的读取速度要快很多。例:

function haseitherclass(element,classname1,classname2){
   var currentclassname = element.classname;
   return currentclassname == classname1 || currentclassname == classname2; 
}

上面element.classname 赋值在currentclassname局部变量,避免了多次查找带来的性能开销。(多次需要全局对象成员,那就赋值在最容易拿到的地方,这样可以减少去搜索和查找)

总结

虽然我还有很多要讲,但是太多太多的方式可以进行性能优化,以后有更好的再补充。不过优化就是跟人找方法用最小的力量去做最大的事情一样,说俗点就是“懒”,我们让程序也懒。

下面是一些关于客户端js性能的一些优化的小技巧:

1.[顶]关于js的循环,循环是一种常用的流程控制。js提供了三种循环:for(;;)、while()、for(in)。在这三种循环中 for(in)的效率最差,因为它需要查询hash键,因此应尽量少用for(in)循环,for(;;)、while()循环的性能基本持平。当然,推 荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++或--运算符。

2.如果需要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免多次查询数组长度。

3.局部变量的访问速度要比全局变量的访问速度更快,因为全局变量其实是window对象的成员,而局部变量是放在函数的栈里的。

4.尽量少使用eval,每次使用eval需要消耗大量时间,这时候使用js所支持的闭包可以实现函数模板。

5.尽量避免对象的嵌套查询,对于obj1.obj2.obj3.obj4这个语句,需要进行至少3次查询操作,先检查obj1中是否包含 obj2,再检查obj2中是否包含obj3,然后检查obj3中是否包含obj4...这不是一个好策略。应该尽量利用局部变量,将obj4以局部变量 保存,从而避免嵌套查询。

6.使运算符时,尽量使用+=,-=、*=、\=等运算符号,而不是直接进行赋值运算。

7.[顶]当需要将数字转换成字符时,采用如下方式:"" + 1。从性能上来看,将数字转换成字符时,有如下公式:("" +) > string() > .tostring() > new string()。string()属于内部函数,所以速度很快。而.tostring()要查询原型中的函数,所以速度逊色一些,new string()需要重新创建一个字符串对象,速度最慢。

8.[顶]当需要将浮点数转换成整型时,应该使用math.floor()或者math.round()。而不是使用parseint(),该方法用于将字符串转换成数字。而且math是内部对象,所以math.floor()其实并没有多少查询方法和调用时间,速度是最快的。

9.尽量作用json格式来创建对象,而不是var obj=new object()方法。因为前者是直接复制,而后者需要调用构造器,因而前者的性能更好。

10.当需要使用数组时,也尽量使用json格式的语法,即直接使用如下语法定义数组:[parrm,param,param...],而不是采用 new array(parrm,param,param...)这种语法。因为使用json格式的语法是引擎直接解释的。而后者则需要调用array的构造器。

11.[顶]对字符串进行循环操作,例如替换、查找,就使用正则表达式。因为js的循环速度比较慢,而正则表达式的操作是用c写成的api,性能比较好。

最后有一个基本原则,对于大的js对象,因为创建时时间和空间的开销都比较大,因此应该尽量考虑采用缓存。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网