zoukankan      html  css  js  c++  java
  • 高效率JavaScript代码的编写技巧

    使用DocumentFragment优化多次append

    添加多个dom元素时,先将元素append到DocumentFragment中,最后统一将DocumentFragment添加到页面。该做法可以减少页面渲染dom元素的次数。经IE和FX下测试,在append1000个元素时,效率能提高10%-30%,FX下提升较为明显。

    前:

    1 for (var i = 0; i < 1000; i++) {
    2     var el = document.createElement('p');
    3     el.innerHTML = i;
    4     document.body.appendChild(el);
    5 }

    后:

    1 var frag = document.createDocumentFragment();
    2 for (var i = 0; i < 1000; i++) {
    3     var el = document.createElement('p');
    4     el.innerHTML = i;
    5     frag.appendChild(el);
    6 }
    7 document.body.appendChild(frag);

    通过模板元素clone,替代createElement

    通过一个模板dom对象cloneNode,效率比直接创建element高。性能提高不明显,约为10%左右。在低于100个元素create和append操作时,没有优势。

    1 var frag = document.createDocumentFragment();
    2 for (var i = 0; i < 1000; i++) {
    3     var el = document.createElement('p');
    4     el.innerHTML = i;
    5     frag.appendChild(el);
    6 }
    7 document.body.appendChild(frag);   
    1 var frag = document.createDocumentFragment();
    2 var pEl = document.getElementsByTagName('p')[0];
    3 for (var i = 0; i < 1000; i++) {
    4     var el = pEl.cloneNode(false);
    5     el.innerHTML = i;
    6     frag.appendChild(el);
    7 }
    8 document.body.appendChild(frag);

    使用一次innerHTML赋值代替构建dom元素

    根据数据构建列表样式的时候,使用设置列表容器innerHTML的方式,比构建dom元素并append到页面中的方式,效率有数量级上的提高。

    1 var frag = document.createDocumentFragment();
    2 for (var i = 0; i < 1000; i++) {
    3     var el = document.createElement('p');
    4     el.innerHTML = i;
    5     frag.appendChild(el);
    6 }
    7 document.body.appendChild(frag);
    1 var html = [];
    2 for (var i = 0; i < 1000; i++) {
    3     html.push('<p>' + i + '</p>');
    4 }
    5 document.body.innerHTML = html.join('');

    使用firstChild和nextSibling代替childNodes遍历dom元素

    约能获得30%-50%的性能提高。逆向遍历时使用lastChild和previousSibling。

    1 var nodes = element.childNodes;
    2 for (var i = 0, l = nodes.length; i < l; i++) {
    3 var node = nodes[i];
    4 ……
    5 }
    1 var node = element.firstChild;
    2 while (node) {
    3 ……
    4 node = node.nextSibling;
    5 }

    使用Array做为StringBuffer,代替字符串拼接的操作

    IE在对字符串拼接的时候,会创建临时的String对象;经测试,在IE下,当拼接的字符串越来越大时,运行效率会急剧下降。Fx和Opera都对字符串拼接操作进行了优化;经测试,在Fx下,使用Array的join方式执行时间约为直接字符串拼接的1.4倍。

    1 var now = new Date();
    2 var str = '';
    3 for (var i = 0; i < 10000; i++) {
    4     str += '123456789123456789';
    5 }
    6 alert(new Date() - now);
    1 var now = new Date();
    2 var strBuffer = [];
    3 for (var i = 0; i < 10000; i++) {
    4     strBuffer.push('123456789123456789');
    5 }
    6 var str = strBuffer.join('');
    7 alert(new Date() - now);

    将循环控制量保存到局部变量

    对数组和列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。

    1 var list = document.getElementsByTagName('p');
    2 for (var i = 0; i < list.length; i++) {
    3     ……
    4 }
    1 var list = document.getElementsByTagName('p');
    2 for (var i = 0, l = list.length; i < l; i++) {
    3     ……
    4 }

    顺序无关的遍历时,用while替代for

    该方法可以减少局部变量的使用。比起效率优化,更能直接看到的是字符数量的优化。该做法有程序员强迫症的嫌疑。

    1 var arr = [1,2,3,4,5,6,7];
    2 var sum = 0;
    3 for (var i = 0, l = arr.length; i < l; i++) {
    4     sum += arr[i];
    5 }   
    1 var arr = [1,2,3,4,5,6,7];
    2 var sum = 0, l = arr.length;
    3 while (l--) {
    4     sum += arr[l];
    5 }

    使用三目运算符替代条件分支

    1 if (a > b) {
    2 num = a;
    3 else {
    4 num = b;
    5 }
    1 num = a > b ? a : b;

    需要不断执行的时候,优先考虑使用setInterval

    setTimeout每一次都会初始化一个定时器。setInterval只会在开始的时候初始化一个定时器。

    1 var timeoutTimes = 0;
    2 function timeout () {
    3     timeoutTimes++;
    4     if (timeoutTimes < 10) {
    5         setTimeout(timeout, 10);
    6     }
    7 }
    8 timeout();
    1 var intervalTimes = 0;
    2 function interval () {
    3     intervalTimes++;
    4     if (intervalTimes >= 10) {
    5         clearInterval(interv);
    6     }
    7 }
    8 var interv = setInterval(interval, 10);

    使用function而不是string

    如果把字符串作为setTimeout和setInterval的参数,浏览器会先用这个字符串构建一个function。

    1 var num = 0;
    2 setTimeout('num++', 10);
    1 var num = 0;
    2 function addNum () {
    3     num++;
    4 }
    5 setTimeout(addNum, 10);

    重复使用的调用结果,事先保存到局部变量

    避免多次取值的调用开销。

    1 var h1 = element1.clientHeight + num1;
    2 var h2 = element1.clientHeight + num2;
    1 var eleHeight = element1.clientHeight;
    2 var h1 = eleHeight + num1;
    3 var h2 = eleHeight + num2;

    使用直接量

    1 var a = new Array(param,param,...) -> var a = []
    2 var foo = new Object() -> var foo = {}
    3 var reg = new RegExp() -> var reg = /.../

    避免使用with

    with虽然可以缩短代码量,但是会在运行时构造一个新的scope。OperaDev上还有这样的解释,使用with语句会使得解释器无法在语法解析阶段对代码进行优化。对此说法,无法验证。

    1 with (a.b.c.d) {
    2 property1 = 1;
    3 property2 = 2;
    4 }
    1 var obj = a.b.c.d;
    2 obj.property1 = 1;
    3 obj.property2 = 2;

    巧用||和&&布尔运算符

    1 function eventHandler (e) {
    2 if(!e) e = window.event;
    3 }
    1 function eventHandler (e) {
    2 e = e || window.event;
    3 }

    还有下面的例子:

    1 if (myobj) {
    2 doSomething(myobj);
    3 }
    1 myobj && doSomething(myobj);

    类型转换

    数字转换成字符串,应用"" + 1,性能上:("" +) > String() > .toString() > new String();

    浮点数转换成整型,不使用parseInt(), parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,建议使用Math.floor()或者Math.round()。

    对于自定义的对象,推荐显式调用toString()。内部操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String。

    循环引用

    如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

    简单的循环引用:

    1 var el = document.getElementById('MyElement');
    2 var func = function () {…}
    3 el.func = func;
    4 func.element = el;

    但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。

    1 function init() {
    2     var el = document.getElementById('MyElement');
    3 el.onclick = function () {……}
    4 }
    5 init();

    init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。

    置空dom对象

    1 function init() {
    2 var el = document.getElementById('MyElement');
    3 el.onclick = function () {……}
    4 }
    5 init();
    1 function init() {
    2 var el = document.getElementById('MyElement');
    3 el.onclick = function () {……}
    4 el = null;
    5 }
    6 init();

    将el置空,context中不包含对dom对象的引用,从而打断循环应用。如果我们需要将dom对象返回,可以用如下方法:

    1 function init() {
    2     var el = document.getElementById('MyElement');
    3     el.onclick = function () {……}
    4     return el;
    5 }
    6 init();
    01 function init() {
    02 var el = document.getElementById('MyElement');
    03 el.onclick = function () {……}
    04 try{
    05 return el;
    06 } finally {
    07     el = null;
    08 }
    09 }
    10 init();

    构造新的context:

    1 function init() {
    2     var el = document.getElementById('MyElement');
    3     el.onclick = function () {……}
    4 }
    5 init();
    1 function elClickHandler() {……}
    2 function init() {
    3     var el = document.getElementById('MyElement');
    4     el.onclick = elClickHandler;
    5 }
    6 init();

    把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。

    通过javascript创建的dom对象,必须append到页面中

    IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!

    01 function create () {
    02     var gc = document.getElementById('GC');
    03     for (var i = 0; i < 5000 ; i++)
    04     {
    05         var el = document.createElement('div');
    06         el.innerHTML = "test";
    07         //下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化
    08         gc.appendChild(el);
    09     }
    10 }

    释放dom元素占用的内存

    将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。

    在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。

    释放javascript对象

    在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。

    对象:obj = null,对象属性:delete obj.myproperty。

    数组item:使用数组的splice方法释放数组中不用的item。

    避免string的隐式装箱

    对string的方法调用,比如'xxx'.length,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时,采用如下写法:

    var myString = new String('Hello World');

  • 相关阅读:
    戴尔笔记本win8.1+UEFI下安装Ubuntu14.04过程记录
    socketpair的使用
    上传App时遇IDFA错误问题
    1-4标签的语法
    TCP协议中的三次握手和四次挥手(图解)
    TsFltMgr.sys系统蓝屏的原因就在于QQ电脑管家!
    STL vector使用方法介绍
    史上最强视频站点真实地址解析
    .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper
    ASP.NET——验证码的制作
  • 原文地址:https://www.cnblogs.com/xiaoyang002/p/4083510.html
Copyright © 2011-2022 走看看