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 相关文章推荐
css图片自适应大小
Nov 28 Javascript
2种jQuery 实现刮刮卡效果
Feb 01 Javascript
js实现滑动触屏事件监听的方法
May 05 Javascript
JavaScript继承模式粗探
Jan 12 Javascript
论Bootstrap3和Foundation5网格系统的异同
May 16 Javascript
浅析script标签中的defer与async属性
Nov 30 Javascript
支持移动端原生js轮播图
Feb 16 Javascript
Vue打包后出现一些map文件的解决方法
Feb 13 Javascript
浅谈super-vuex使用体验
Jun 25 Javascript
jQuery 实现倒计时天,时,分,秒功能
Jul 31 jQuery
JavaScript实现淘宝京东6位数字支付密码效果
Aug 18 Javascript
vue实现路由监听和参数监听
Oct 29 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 base64编码后解码乱码的解决办法
2014/06/19 PHP
phpcms手机内容页面添加上一篇和下一篇
2015/06/05 PHP
PHP弹出对话框技巧详细解读
2015/09/26 PHP
Zend Framework使用Zend_Loader组件动态加载文件和类用法详解
2016/12/09 PHP
php基于PDO实现功能强大的MYSQL封装类实例
2017/02/27 PHP
yii2.0整合阿里云oss的示例代码
2017/09/19 PHP
jQuery代码优化 遍历篇
2011/11/01 Javascript
JavaScript高级程序设计(第3版)学习笔记6 初识js对象
2012/10/11 Javascript
jquery使用slideDown实现模块缓慢拉出效果的方法
2015/03/27 Javascript
Node.js和MongoDB实现简单日志分析系统
2015/04/25 Javascript
javascript删除数组重复元素的方法汇总
2015/06/24 Javascript
js鼠标点击图片切换效果代码分享
2015/08/26 Javascript
理解javascript中DOM事件
2015/12/25 Javascript
vue2.0 better-scroll 实现移动端滑动的示例代码
2018/01/25 Javascript
解决layui表格内文本超出隐藏的问题
2019/09/12 Javascript
在vue和element-ui的table中实现分页复选功能
2019/12/04 Javascript
封装一下vue中的axios示例代码详解
2020/02/16 Javascript
Python采用raw_input读取输入值的方法
2014/08/18 Python
Python实现豆瓣图片下载的方法
2015/05/25 Python
Python的多维空数组赋值方法
2018/04/13 Python
python多个模块py文件的数据共享实例
2019/01/11 Python
python3正则提取字符串里的中文实例
2019/01/31 Python
Python3.5内置模块之random模块用法实例分析
2019/04/26 Python
python中sympy库求常微分方程的用法
2020/04/28 Python
python logging.info在终端没输出的解决
2020/05/12 Python
使用keras实现densenet和Xception的模型融合
2020/05/23 Python
浅谈Python中的字符串
2020/06/10 Python
Python开发.exe小工具的详细步骤
2021/01/27 Python
澳大利亚女士时装在线:Rockmans
2018/09/26 全球购物
大学毕业感言200字
2014/03/09 职场文书
成立公司计划书
2014/05/07 职场文书
安全生产先进个人总结
2015/02/15 职场文书
2015年教育实习工作总结
2015/04/24 职场文书
基于Redis实现分布式锁的方法(lua脚本版)
2021/05/12 Redis
解析Redis Cluster原理
2021/06/21 Redis
叶县这家生产军用电台的兵工厂,人称“四机部”,走出一上将
2022/02/18 无线电