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 实现复制到粘贴板的功能代码
May 13 Javascript
javascript 传统事件模型构造的事件监听器实现代码
May 31 Javascript
Javascript计算时间差的函数分享
Jul 04 Javascript
动态的创建一个元素createElement及删除一个元素
Jan 24 Javascript
用jQuery与JSONP轻松解决跨域访问的问题
Feb 04 Javascript
JS运动基础框架实例分析
Mar 03 Javascript
JavaScript简单遍历DOM对象所有属性的实现方法
Oct 21 Javascript
深入浅析javascript中的作用域(推荐)
Jul 19 Javascript
几种响应式文字详解
May 19 Javascript
Node.js如何实现注册邮箱激活功能 (常见)
Jul 23 Javascript
javaScript实现滚动条事件详解
Mar 24 Javascript
Angular 利用路由跳转到指定页面的指定位置方法
Aug 31 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数组函数序列之array_slice() - 在数组中根据条件取出一段值,并返回
2011/11/07 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
PHP分页详细讲解(有实例)
2013/10/30 PHP
Zend Framework+smarty用法实例详解
2016/03/19 PHP
用JavaScript和注册表脚本实现右键收藏Web页选中文本
2007/01/28 Javascript
关于IE7 IE8弹出窗口顶上
2008/12/22 Javascript
Jquery公告滚动+AJAX后台得到数据
2011/04/14 Javascript
JS随即打乱数组实现代码
2012/12/03 Javascript
jquery实现图片等比例缩放以及max-width在ie中不兼容解决
2013/03/21 Javascript
javascript动态添加样式(行内式/嵌入式/外链式等规则)
2013/06/24 Javascript
一个html5播放视频的video控件只支持android的默认格式mp4和3gp
2014/05/08 Javascript
JS解析XML实例分析
2015/01/30 Javascript
谈谈AngularJs中的隐藏和显示
2015/12/09 Javascript
js实现prototype扩展的方法(字符串,日期,数组扩展)
2016/01/14 Javascript
微信小程序 wxapp视图容器 view详解
2016/10/31 Javascript
给easyui datebox扩展一个清空的实例
2016/11/09 Javascript
微信小程序canvas写字板效果及实例
2017/06/15 Javascript
React 使用Hooks简化受控组件的状态绑定
2019/03/18 Javascript
js用正则表达式筛选年月日的实例方法
2021/01/04 Javascript
Vue基本指令实例图文讲解
2021/02/25 Vue.js
极简的Python入门指引
2015/04/01 Python
python使用xlrd实现检索excel中某列含有指定字符串记录的方法
2015/05/09 Python
Python实现通过文件路径获取文件hash值的方法
2017/04/29 Python
python 给DataFrame增加index行名和columns列名的实现方法
2018/06/08 Python
Python简单计算给定某一年的某一天是星期几示例
2018/06/27 Python
Django框架models使用group by详解
2020/03/11 Python
jupyter 导入csv文件方式
2020/04/21 Python
python中Array和DataFrame相互转换的实例讲解
2021/02/03 Python
马来西亚网上购物:Youbeli
2018/03/30 全球购物
西班牙鞋子和箱包在线销售网站:zapatos.es
2020/02/17 全球购物
PPP协议组成及简述协议协商的基本过程
2015/05/28 面试题
教师简历自我评价
2014/02/03 职场文书
经典安踏广告词
2014/03/21 职场文书
环保专项行动方案
2014/05/12 职场文书
事业单位年度考核评语
2014/12/31 职场文书
PostgreSQL聚合函数介绍以及分组和排序
2022/04/12 PostgreSQL