如何优化js代码
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优化js代码相关的知识,希望对你有一定的参考价值。
一、 让代码简洁:一些简略的表达方式也会产生很好的优化eg:x=x+1;在不影响功能的情况下可以简写为x++;
二、 变量名方法名尽量在不影响语意的情况下简单。(可以选择首字母命名)
eg:定义数组的长度可以取名为:ArrLen而不需要取为ArrayLength。
三、 关于JS的循环,循环是一种常用的流程控制。
JS提供了三种循环:for(;;)、while()、for(in)。在这三种循环中for(in)的效率最差,因为它需要查询Hash键,因此应尽量少用for(in)循环,for(;;)、while()循环的性能基本持平。当然,推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++或–运算符。
四、 如果需要遍历数组,应该先缓存数组长度,将数组长度放入局部变量中,避免多次查询数组长度。
因为我们常常要根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询a.length,就要额外进行一个操作,而预先把var len=a.length,则就少了一次查询。
五、 尽量选用局部变量而不是全局变量。
局部变量的访问速度要比全局变量的访问速度更快,因为全局变量其实是window对象的成员,而局部变量是放在函数的栈里的。
六、 尽量少使用eval。
每次使用eval需要消耗大量时间,这时候使用JS所支持的闭包可以实现函数模板。
七、 减少对象查找
因为javascript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。
八、 字符串连接。
如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr。
如果要连接多个字符串,应该少使用+=,如s+=a;s+=b;s+=c;应该写成s+=a + b + c;
而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存。怎么用呢?使用JavaScript数组来收集,最后使用join方法连接起来,如下
var buf = new Array();for(var i = 0; i < 100; i++) buf.push(i.toString());var all = buf.join("");
九、 类型转换
1. 把数字转换成字符串,应用"" + 1,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:("" +) > String() > .toString() > new String()
尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。
String()属于内部函数,所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。
2. 浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()。Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。
3. 对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高
十、 尽量作用JSON格式来创建对象,而不是var obj=new Object()方法。
因为前者是直接复制,而后者需要调用构造器,因而前者的性能更好。
十一、 当需要使用数组时,也尽量使用JSON格式的语法,
使用JSON格式的语法即直接使用如下语法定义数组:[parrm,param,param...],而不是采用new Array(parrm,param,param...)这种语法。因为使用JSON格式的语法是引擎直接解释的。而后者则需要调用Array的构造器。
十二、 对字符串进行循环操作,例如替换、查找,就使用正则表达式。
因为JS的循环速度比较慢,而正则表达式的操作是用C写成的API,性能比较好。
十三、 插入html
很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。
十四、 对象查询
使用[“”]查询要比.items()更快
十五、 定时器
如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval。setTimeout每次要重新设置一个定时器。
十六、 尽量减少DOM调用
在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作。可是对DOM的操作是非常昂贵的,因为这会导致浏览器执行回流 (reflow)操作。我们应该尽可能的减少DOM操作。 参考技术A 客户端脚本能让你的应用更加地动态和活跃, 但是浏览器对代码的解析可能造成效率问题, 而这种性能差异在客户端之间也不尽相同。所以我们要尽可能的优化js代码,下面是对JavaScript代码优化的原则:(1)为IE6(未打补丁的JScript 5.6或更早版本)做优化!与其他语言不同,JS的效率很大程度是取决于JS engine的效率。除了引擎实现的优劣外,引擎自己也会为一些特殊的代码模式采取一些优化的策略。例如FF、Opera和Safari的JS引擎,都对字符串的拼接运算(+)做了特别优化。所以对于不同的引擎,所作的优化极有可能是背道而驰的。而如果做跨浏览器的web编程,则最大的问题是在于IE6(JScript 5.6)!因为在不打hotfix的情况下,JScript引擎的垃圾回收的bug,会导致其在真实应用中的performance跟其他浏览器根本不在一个数量级上。因此在这种场合做优化,实际上就是为JScript做优化!(2)以大规模循环体为最主要优化对象。如果你的程序已经优化到在IE6下可以接受的性能,那基本上在其他浏览器上性能就完全没有问题。因此,注意下面讲的许多问题在其他引擎上可能完全不同,例如在循环中进行字符串拼接,通常认为需要用Array.join的方式,但是由于SpiderMonkey等引擎对字符串的+运算做了优化,结果使用Array.join的效率反而不如直接用+!但是如果考虑IE6,则其他浏览器上的这种效率的差别根本不值一提。JS优化与其他语言的优化也仍然有相同之处。优化的关键,仍然是要把精力放在最关键的地方,也就是瓶颈上。一般来说,瓶颈总是出现在大规模循环的地方。这倒不是说循环本身有性能问题,而是循环会迅速放大可能存在的性能问题。(注:以下的优化原则,只在大规模循环中才有意义,在循环体之外做此类优化基本上是没有意义的)(3)尽量避免过多的引用层级和不必要的多次方法调用目前绝大多数JS引擎都是解释执行的,而解释执行的情况下,在所有操作中,函数调用的效率是较低的。此外,过深的prototype继承链或者多级引用也会降低效率。JScript中,10级引用的开销大体是一次空函数调用开销的1/2。这两者的开销都远远大于简单操作(如四则运算)。(4)尽量使用语言本身的构造函数和内建函数。值得注意的是,有些情况下看似是属性访问,实际上是方法调用。例如所有DOM的属性,实际上都是方法。在遍历一个NodeList的时候,循环条件对于nodes.length的访问,看似属性读取,实际上是等价于函数调用的。而且IE DOM的实现上,childNodes.length每次是要通过内部遍历重新计数的。(My god,但是这是真的!因为我测过,childNodes.length的访问时间与childNodes.length的值成正比!)这非常耗费。所以预先把nodes.length保存到js变量,当然可以提高遍历的性能。同样是函数调用,用户自定义函数的效率又远远低于语言内建函数,因为后者是对引擎本地方法的包装,而引擎通常是c,c++,java写的。进一步,同样的功能,语言内建构造的开销通常又比内建函数调用要效率高,因为前者在JS代码的parse阶段就可以确定和优化。(5)尽量减少不必要的对象创建。本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……本回答被提问者采纳
如何优化自己的JS代码
尽管接触大大小小项目N多个,但是刚入行两年,
撸码还是没有完全成一定的规律;最近受到很多启发,打算沉淀沉淀自己的代码;
之前很多页面的很多js脚本本分代码,更注重效果,事件久后没有发展 性能也是很关键的一部分;
之前接触的大多是网站微站门户app的项目,从某种角度来讲 一次(多次)成型后就上线了,不注重逻辑层是否真的合理,精简;
自从接触平台类型项目 结构层 、表示层、行为层的优化一个比一个重要。
逻辑层代码需要提高注意的地方——代码的封装及方法的兼容性。
一个非常简单的抽屉效果
<script> $(function () { $(".ability_content .ability_title").click(function () { var data_state = $(this).attr("data-state"); if (data_state == 0) { $(this).children(".icon").text("+"); $(this).attr("data-state", "1"); $(this).siblings("ul").slideUp(); } else if (data_state == 1) { $(this).children(".icon").text("-"); $(this).attr("data-state", "0"); $(this).siblings("ul").slideDown(); } }); }) </script>
修改之前粗糙的写法:
<script> $(function () { $(".toggled").click(function () { if ($(this).parent().next().css("display") == "block") { $(this).parent().next().slideUp(500); $(this).html("+"); } else { $(this).parent().next().slideDown(500); $(this).html("-"); } }) }) </script>
定义变量data_state能将需要做判断的属性保存到内存中,减少reflow回流 从而减轻CPU负担
同样的思路去写 信息是否保存成功的案例
<script> $(function () { //保存状态判断 (function () { //获取保存状态 var hd_saveState = $("#hd_state").val(); if (hd_saveState == "0") { //保存失败 layer.open({ type: 1 , title: "提示" , content: "<div style=‘text-align:center;padding:20px;‘>保存失败!</div>" , area: [‘300px‘, ‘auto‘] , btn: [‘确定‘] , yes: function (index, layero) { layer.close(index); } }); $("#hd_state").val("-1"); } else if (hd_saveState == "1") { //保存成功 layer.open({ type: 1 , title: "提示" , content: "<div style=‘text-align:center;padding:20px;‘>保存成功!</div>" , area: [‘300px‘, , ‘auto‘] , btn: [‘确定‘] , yes: function (index, layero) { layer.close(index); } }); $("#hd_state").val("-1"); } })(); }) </script>
检索 事件 自刷新 封装取值便捷思路:
<script> $(function () { $(".seach_wrap button").click(function () { //获取姓名 var nameSearCh = $("#nameSearCh").val(); //获取学历 var EduSearch = $("#EduSearch").val(); //获取简历授权 var enPowerSearch = $("#enPowerSearch").val(); //获取简历状态 var StateSearch = $("#StateSearch").val(); window.location.href = "/Yuan/Pages/talent/Resume_management.aspx?page=0" + getpram(‘searchText‘, nameSearCh) + getpram(‘edu‘, EduSearch) + getpram(‘empower‘, enPowerSearch) + getpram(‘state‘, StateSearch); function getpram(key, value) { var str = ""; if (value) { str = "&" + key + "=" + value; } return str; } }); }); </script>
可通过 getpram函数方便获取要取得四个值对应得value 并返回重载的href中。
交互部分
做前端写交互可谓家常便饭了,交互的步骤不多,但每次用起来都不一样,只是换汤不换药。但自己每次都被搞迷,接触太少,一下是整理了 关闭或发行功能按钮的交互代码:
//关闭或发行操作 $(".oprate_wrap button").click(function () { var thisText = $(this).text().trim(); var metaId = $(this).attr("data-mid"); if (thisText != "查看") { postOprate(metaId, thisText, $(this)); // postOprate 函数包含三个对象组成部分 如下: } }); /** * 根据metaId和操作类型 进行 智券的发行或者关闭 * @param metaId * @param oprateText */ function postOprate(metaId, oprateText, clickObj) { var data = { "mid": metaId, "state": oprateText } data = JSON.stringify(data); $.ajax({ type: "post", url: "/Yuan/Pages/talent/Coupon_management.aspx/Product_UpdateSate", //data: JSON.stringify(obj), data: "{ ‘data‘: ‘" + data + "‘}", dataType: "json", contentType: "application/json; charset=utf-8", success: function (data) { if (data.d) { var getDatas = data.d.split("*"); if (getDatas[0] == 0) { pop(getDatas[1]); } else if (getDatas[0] == 1) { pop(getDatas[1]); if (oprateText == "关闭") { clickObj.html("发行"); } else { clickObj.html("关闭"); } } } }, error: function (data) { console.log(data) pop(oprateText + "失败!"); } }) }
以上是关于如何优化js代码的主要内容,如果未能解决你的问题,请参考以下文章