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


Posted in Javascript onAugust 30, 2020

最近刚阅读完《高性能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对象,因为创建时时间和空间的开销都比较大,因此应该尽量考虑采用缓存。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js tab 选项卡
Apr 26 Javascript
javascript实现面向对象类的功能书写技巧
Mar 07 Javascript
jquery动态改变form属性提交表单
Jun 03 Javascript
js对字符的验证方法汇总
Feb 04 Javascript
学习JavaScript设计模式之装饰者模式
Jan 19 Javascript
分享JavaScript监听全部Ajax请求事件的方法
Aug 28 Javascript
JS只能输入正整数的简单实例
Oct 07 Javascript
详解webpack+angular2开发环境搭建
Jun 28 Javascript
vue小图标favicon不显示的解决方案
Sep 19 Javascript
JavaScript 九种跨域方式实现原理
Feb 11 Javascript
在vue中使用setInterval的方法示例
Apr 16 Javascript
express + jwt + postMan验证实现持久化登录
Jun 05 Javascript
如何检测JavaScript中的死循环示例详解
Aug 30 #Javascript
JavaScript中CreateTextFile函数
Aug 30 #Javascript
详解vue组件之间的通信
Aug 30 #Javascript
如何阻止移动端浏览器点击图片浏览
Aug 29 #Javascript
JavaScript事件委托实现原理及优点进行
Aug 29 #Javascript
JS如何判断对象是否包含某个属性
Aug 29 #Javascript
JS获取当前时间戳方法解析
Aug 29 #Javascript
You might like
php中理解print EOT分界符和echo EOT的用法区别小结
2010/02/21 PHP
jQuery 源码分析笔记
2011/05/25 PHP
详解PHP神奇又有用的Trait
2019/03/25 PHP
jqPlot Option配置对象详解
2009/07/25 Javascript
JavaScript中的prototype使用说明
2010/04/13 Javascript
JS跨域代码片段
2012/08/30 Javascript
PHP中使用微秒计算脚本执行时间例子
2014/11/19 Javascript
浅谈jquery中delegate()与live()
2015/06/22 Javascript
JS实现带有3D立体感的银灰色竖排折叠菜单代码
2015/10/20 Javascript
jQuery获取父元素及父节点的方法小结
2016/04/14 Javascript
jquery插件方式实现table查询功能的简单实例
2016/06/06 Javascript
js改变style样式和css样式的简单实例
2016/06/28 Javascript
localStorage实现便签小程序
2016/11/28 Javascript
JS分页的实现(同步与异步)
2017/09/16 Javascript
4个顶级JavaScript高级文本编辑器
2018/10/10 Javascript
JavaScript Canvas编写炫彩的网页时钟
2019/10/16 Javascript
Python中使用HTMLParser解析html实例
2015/02/08 Python
Python中zfill()方法的使用教程
2015/05/20 Python
Django的信号机制详解
2017/05/05 Python
Python tkinter模块弹出窗口及传值回到主窗口操作详解
2017/07/28 Python
对pandas的dataframe绘图并保存的实现方法
2017/08/05 Python
详解Python 序列化Serialize 和 反序列化Deserialize
2017/08/20 Python
Python在groupby分组后提取指定位置记录方法
2018/04/20 Python
python使用循环打印所有三位数水仙花数的实例
2018/11/13 Python
Python3.5 Json与pickle实现数据序列化与反序列化操作示例
2019/04/29 Python
使用sklearn的cross_val_score进行交叉验证实例
2020/02/28 Python
Python基于numpy模块实现回归预测
2020/05/14 Python
解决python便携版无法直接运行py文件的问题
2020/09/01 Python
乌克兰珠宝大卖场:Zlato.ua
2020/09/27 全球购物
Ado与Ado.net的相同与不同
2014/12/08 面试题
工程部主管岗位职责
2013/11/17 职场文书
上班离岗检讨书
2014/09/10 职场文书
四风对照检查剖析材料
2014/10/07 职场文书
合同和协议有什么区别?
2014/10/08 职场文书
2015自愿离婚协议书范本
2015/01/28 职场文书
预备党员的思想汇报,你真的会写吗?
2019/06/28 职场文书