never-online 's Tech Blog In CSDN

never online - Everlasting love for angela.

用户操作
[即时聊天] [发私信] [加为好友]
never-onlineID:BlueDestiny
153984次访问,排名502好友17人,关注者33
在京城
BlueDestiny的文章
原创 114 篇
翻译 0 篇
转载 8 篇
评论 224 篇
never-online的公告
Web:
www.never-online.net
Email:
BlueDestiny##126.com
QQ:
77091373
最近评论
kwlong2008:来看看朋友,最近过的怎么样?俺的空间是介绍网站制作网站建设的,有时间也常去一下我的空间谢谢,wow gold

When you play a game of the day, the list of friends in the game whether there are always a few names of black
wow power leveling<……
zyhomepage:支持一下
clicksun:如果要制作纯CSS+DIV的圆角方框图形,可以到这里直接在线制作,什么颜色都可以:http://corner.cha.la
C_SuperMe:可以好好学习一下啊!!
文章分类
收藏
相册
icon
JS & DHTML
Dhteumeuleu
Douglas Crockford
Webfx
常去之地
ASP.Net
MSDN英文
XML指南
XML的大本营
博客园
正则表达式
我的网站
我网站的Blog(RSS)
友情链接
KimSoft的blog(RSS)
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 javascript效率经验谈,欢迎讨论和提供好的思路 8-15更新JS效率个人经验谈(8-15更新),加入range技巧收藏

新一篇: 你所要知道JS(DHTML)中的一些技巧  | 旧一篇: 慎用正则的test方法

首先,要谢谢CSDN hbhbhbhbhb1021(天外水火(我要多努力))和cuixiping(无心)的提醒。我会抽空把IE专有的方法如:insertAdjacentHTML的速度也给测出来看看是否合适大量数据时IE下,不用innerHTML的速度。
这里的主要测试不是指生成数据时的速度,指的是匹配速度 ,例如
我这里的匹配速度
我测的10000条数据,有效数据为1000-1100条,输出复杂的HTML,速度为360ms左右,方法为 正则匹配Match(有循环)
希望贴出您的测试数据。
行innerHTML和insertAdjacentHTML速度的测试,比均结果相差不会大于20ms(平均速度),在IE中insertAdjacentHTML速度还是很快的,在Mozilla下是得不偿失的。

可以点击这里进行简单的匹配测试
点击这里进行innerHTML和insertAdjacentHTML速度的测试,可以兼容Mozilla的

写这篇文章,其间我也是删删减减的,所以语句也不怎么通顺,看的朋友也就辛苦了一些了。

本文主要是出于有朋友使用我原来写的autocomplete的JS控件。当数据量大的时候,会出现效率极其慢的情况,我在这段时间做出的一些测试也及一些经验,与大家分享,如果有错的地方,还请指出。

经过测试,我们会发现如下的情况或者说的结论,如果您的测试结果与我的不符,请说明原因,以便相互学习。

1)当一个较大的HTML字符串给到obj.innerHTML时会出现麻烦。也就是说当一个较大的字符串在赋予一个Element的innerHTML时,这个过程将可能是我们无法容忍的。(而事实上这并非JS的错,而确实是String数据量太多)
2)用拼合字符串的方法可以使效率提高,在字符串较大时,2)的情况仍然出现。超过一定的数量,速度会明显慢下来。
3)正则匹配的方法会比平常遍历的方法要高效一些。
4)在执行过程中,绑定事件的时间会花费更多一些。测试在1w条数据情况下,大约是匹配以及生成HTML数据的30倍,也就是说生成数据总花费100ms,而绑定事件则需要3000ms。
5)总体来说。IE的速度要比Mozilla要慢(我用的是Firefox1.5做的测试)。
6)大数据量时,不要用DOM生成Element。
7)非JS内置方法,也许会引发很多时间过多重复的劳动而且可能事得其反。建议尽可能利会内置方法。
总结问题:
一、在把字符串给到innerHTML上。
二、循环绑定事件所花费时间。
三、生成我们需要的DIV所花时间。
四、不同的浏览器问题。

下面对症下药:

问题一

我们可以做的没有其它的,只有尽可能少的HTML字符串,比如最基本的一个DIV,可以这样写
<div style="height:20px; font:9pt Verdana;"></div>也可以这样写<div class="c1"></div>,第二种就比第一种速度明显要快的。如果还不行的话,请看下面这个方法对你是否合适

在做程序的时候突然想起来51js上PK tree,一位版主所写的一棵树,1百万的一个节点,动态载入。只需要不到1秒。毫无疑问,肯定是取巧了,因为只要只生tree的html就是一个很大的数量。这个树的特别的地方就是生成树时,并非把1百万的节点都一次生成innerHTML,而是只生成在视角范围内的节点,当滚动条向下滚的时候,才动态的再生成树节点。这个方法至少我觉得思想很开阔,很有价值。

我们所知道,mySQL数据库里取数据可以这么取。SELECT * FROM table limit 0,100,意思是只取数据库中的0-100条数据。说到这里可能有些朋友也想到了,在JS中,我们可以利用这个方法来取数据,将一个数组看作是一个表。只是单纯的数据表,非二维表。如图
uploads/200608/07_194917_array_limit.png

利用这一些,我们可以把数据有效的值先取出来。如图:
uploads/200608/07_194953_auto_limit.png

想想看。假如我们取一个数组,下标为10000,设生成一个autocomplete的节点HTML长度20(已经非常小了"<div class="out">item</div>)。
匹配数据已知:有3000条数据
输节字节数为:3000(asc码)也就是3000*20=60000字节
而用limit方法,输出为:10*20=200字节。
很明显的差距!
之后我们便可以分步求解,即当滚动条出现,或者按下down(方向键)再动态的生成innerHTML。
8-13更新:测了一下,用自己写的limit的速度,和自带的Array.slice的速度比了一下,速度差不多,而且有的时候还比slice的速度还要快一些。
Array.prototype.limit = function(l, h) {
var _a = this; var ret = [];
l = l<0?0:l; h = h>_a.length?_a.length:h;
for (var i=0; i<_a.length; i++) {
if (i>=l && i<=h) ret[ret.length] = _a[i];
if (i>h) break;
}; return ret;
}
有兴趣的朋友也可以自己测一下,贴出数据,看看哪个效率更好。

问题二、
为什么我们还要循环来绑定事件呢?
还是由于问题一。
假设这样写
1)
<div id="container">
<div onclick="handlerClick()">never-online</div>
</div>
还可以这样写
2)
<div id="container">
<div>never-online</div>
</div>
document.getElementById("container").childNodes[0].onclick=function(){handlerClick()};
这样也可以省掉一些字符串,从而节省字符串资源。但又需要把container的子元素再遍历,所以也会花费时间,用第一种方法还是第二种?我建议还是用第一种,但最好把字符串减到最低,如:
<div id="container">
<div onclick="_c()">never-online</div>
</div>
大数据量情况下,还是越少字符越好,虽然代码不怎么美观。

问题三、
生成DIV时我们可以这样生成
var div = document.createElement("DIV");
div.onclick=function(){};
//TODO
也可以这样用字符串
var sHtml = "<div onclick=foo()>val</div>";
当数量小时,第一种速度会比第二种快。但当达到一个数量级时,第二种要明显比第一种快。总体来说第二种较好。因为第二种还可以更灵活,比如利用join,还有正则匹配。

问题四、
这个问题也不容忽视的。每个browser有不同的特点,速度执行也有不同,我个人觉得,这点和JS上优化效率上是一样的。
尽可能的利用浏览器本身的内置方法,这样大多数情况下也可以把效率提高。

那么如何能够把脚本的效率提高起来呢?
1)用match匹配,一个aCache数组。循环match.length,并给aCache,之后用join(""),再给到innerHTML(此方法仍然需要循环,而且需额外的一个数组做临时数据存储)
2)无需循环,但必须在生成数据时也额外生成指定字符串。(此方法也需要额外的空间做临时数据)如图:
uploads/200608/09_002205_match.png

3)宁可多次判断,也不重复进行一次重新匹配。e.g:
input控件中第一次取到的值为:1,第二次按下的值为12
如果进行判断的话,可以事件存储一个值,也就是前一次按下的值。如上面的值1。第二次按下时没有退格,即再在前面的值中加一个字符2,那么我们将在前面1中匹配出的数据中进行匹配。这样可以大大的减轻循环的次数。
4)利用问题一中所写的limit,将数据动态取出。这些能够很好的解决HTML字符串过大的问题,但此方法控制不当的话,也会事得其反。
5) 利用range技巧来加入HTMLStr,也就是说,当一个HTML字符串太大时,再用innerHTML+=anotherHTMLStr,这样的方法,也是会让速度太慢,在IE中,我们可以用obj.insertAdjacentHTML("beforeEnd", anotherHTMLStr)这样的方法来插入HTML,这个方法经过测试,比较稳定。而在Mozilla中,则要利用range的技巧来达到此目的,如下:
if (browser.isMozilla) {
 HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sHTML) {
 var df; var r = this.ownerDocument.createRange();
 switch (String(sWhere).toLowerCase()) {
 case "beforebegin":
 r.setStartBefore(this);
 df = r.createContextualFragment(sHTML);
 this.parentNode.insertBefore(df, this);
 break;
 case "afterbegin":
 r.selectNodeContents(this);
 r.collapse(true);
 df = r.createContextualFragment(sHTML);
 this.insertBefore(df, this.firstChild);
 break;
 case "beforeend":
 r.selectNodeContents(this);
 r.collapse(false);
 df = r.createContextualFragment(sHTML);
 this.appendChild(df);
 break;
 case "afterend":
 r.setStartAfter(this);
 df = r.createContextualFragment(sHTML);
 this.parentNode.insertBefore(df, this.nextSibling);
 break;
 }
 };
}
后记:效率问题没有一个完整的解决方法,只有实践中根据需要而定。因此,上面的方法仅供您参考,如果你也有一些好的方法,可以在评论中写下您的经验,以便交流。

发表于 @ 2006年08月09日 01:39:00|评论(loading...)|编辑

新一篇: 你所要知道JS(DHTML)中的一些技巧  | 旧一篇: 慎用正则的test方法

评论

#风之石 发表于2006-08-09 09:52:00  IP: 218.204.98.*
分析的很好,很有价值!
#fc8264.meibu.com 发表于2006-08-14 12:12:00  IP: 219.236.16.*
收藏!! (开源的自定义web表单工具,在: http://fc8264.meibu.com )
#js 发表于2006-08-14 16:52:00  IP: 203.86.41.*
偶觉得大数据量还是避免js的好!
毕竟js是脚本!
#dup 发表于2006-08-14 16:59:00  IP: 210.78.32.*
document.getElementById("container").childNodes[0].onclick=function(){handlerClick()};

为什么要采用这个方式?
为什么不使用addEventListener和removeEventListerner这个符合标准的方法?或者用attachEvent和detachEvent这个MS的变形?

你的array join方法非常聪明:)

但是有几个问题:
如果array非常大,是不是会占用大量内存?
对于array,如果其数据不是一维的,而是不规整的多维的(不好意思,我最近的项目中就用array表达树了)该怎么办?
join有办法按照我规定的方式处理元素的文本表示么?或者更详细的说,我的array中的元素是对象,而不是原始值,我能规定这些元素怎么转换成string么?
#BlueDestiny 发表于2006-08-15 16:00:00  IP: 219.159.90.*
@dup

为什么不使用addEventListener和removeEventListerner这个符合标准的方法?或者用attachEvent和detachEvent这个MS的变形?
------------
你可以试试用这个注册事件的方法,看看哪个方法用的内存多。而且还有memory leak在里面。

如果array非常大,是不是会占用大量内存?
-----------------
array可以由slice或者写出的limit控制。

如果其数据不是一维的,而是不规整的多维
-----------------
js所有的数组都是由Object而来,也就是一个array就是一个Object,所以对于这种情况下,array的"多维"要慎用。array有时也是在JS中处理数据中唯一的办法


还有一个方法就是事先把所有的事件,以及css绑到div上,输出时就不需要每次都绑定了,这也是一个方法的,不过我没有做出测试数据出来。IE中比在Mozilla中的速度表现得较不稳定,但insertAdjacenHTML在IE中又表现得比Mozilla中好。
事实上,aCache.join("<div></div>")这样比我后面所说的replace还要快。
#fat4j 发表于2006-08-20 17:21:00  IP: 211.91.133.*
您好,我有个js的问题想请教您一下
也许这里不是提问的合适的地方 但是我实在找不到更好的向您咨询的好方式了.
关于with. 下面的代码在 ie 上可以运行 在ff上就不行 .怎么办??

with (一个对象) {
tsetMethod();

function tsetMethod(){
alert("ok");
}
}

在ff里 必须要将 tsetMethod(); 与 函数定义调换位置.
如果function 在 with外 则无所谓在前还是在后

我现在有个问题就是 这个 function的定义必须要在 with内出现 而且位置不确定, 我希望在with内任何地方调用他都可以 ,在ff中该怎么办呢?
PS: "function的定义必须要在 with内出现 而且位置不确定" 这是因为代码是程序生成的,具体说来比较复杂,我就不细说了,总之我不能人为的控制 function的出现位置.

#BlueDestiny 发表于2006-08-20 19:54:00  IP: 219.159.89.*
@fat4j:
这个不是with所引发的问题,而是IE与Mozilla的脚本解释的方法不一样。你可以这样来兼容上面的问题,相当于内联函数:

o={
foo: function(){
alert("never-online");
}
};
with (o) {
(function tsetMethod(){
alert("ok");
})()
foo();
}
#fat4j 发表于2006-08-20 20:52:00  IP: 211.91.133.*
这个方法我也想过了
可是 tsetMethod 方法要被多次运行
我总不能在每次运行的地方都写
(function tsetMethod(){
alert("ok");
})()
吧 :(

我觉得这个地方 ff应该该一下 向ie 学习
如果我是用
var tsetMethod=function的方式来定义 顺序敏感还可以
我都用 function tsetMethod 了还这样 郁闷啊

没有更好的办法了吗??? tears....
#BlueDestiny 发表于2006-08-20 21:29:00  IP: 219.159.89.*
也可以这样也做吧。
<script type="text/javascript">
o={};
a=[];
with (o) {
try{ tsetMethod(); } catch(ex) { a.push('tsetMethod()'); }
function tsetMethod(){
alert("ok");
}
}
onload=function(){
for (var i=0; i<a.length; i++)
eval(a[i]);
}
</script>
#fat4j 发表于2006-08-20 20:53:00  IP: 211.91.133.*
忘了谢谢你的回复了

谢谢 :)
#BlueDestiny 发表于2006-08-20 21:42:00  IP: 219.159.89.*
或者还有这样的方法,在该script标签onload时再触发事件,即
script=document.getElementsByTagName("SCRIPT");
script=script[script.length-1];
script.onload=function(){/*TODO*/}
或者这样
<script type="text/javascript">
o={};
with (o) {
setTimeout('tsetMethod()',2000);
function tsetMethod(){
alert("ok");
}
}
</script>
#fat4j 发表于2006-08-20 21:44:00  IP: 211.91.133.*

我提另做一个专门存放 function的string数组(这个你可能没太明白 数组和着问题有什么关系?? )吧

其实我最近在做一个新的 js template (一个用js写的类似freemarker的咚咚)

原来那个项目死了 好久没人更新了 我打算把他拣起来 继续改进

以后可能还有很多js方面的问题要烦扰你 先谢谢了

顺便再问个问题 呵呵

正则表达式中 的 [^abc] 是不接受abc集合 也就是不接受a b c三个字母
那我怎么写正则表达式 来让他 不接受的是 "abc" 这个词呢??

[^(abc)]这样不行啊



#BlueDestiny 发表于2006-08-20 21:58:00  IP: 219.159.89.*
那个数组,主要存储执行错误的函数,等到合适的时候再触发该函数,并不一定要onload之后,只需要在该script的readyState=="complete"的时候触发(当然这个readyState是Mozilla不支持的,我的意思是这个就可以了),或者setTimout来执行,其实Mozilla不支持的原因是脚本在解释的情况下,是因为该函数没有解释完毕。(因为setTimout情况下,可以执行)

[^abc]不是指abc这个词,而是a,b,c
#fat4j 发表于2006-08-21 08:27:00  IP: 211.91.133.*
谢谢你 :)

那我要是就是想排除 连续的 abc 该怎样写正则呢:(
#BlueDestiny 发表于2006-08-21 23:32:00  IP: 219.159.89.*
贴了N次都没有贴上去~~~,CSDN的blog真让人又爱又恨。

可以用负向预查
?!abc这在有的时候可以看作是[^]的扩充。
但要注意的是?!从非abc的字符开始搜索。
#dh20156 发表于2006-12-21 14:42:41  IP: 218.204.98.*
BlueDestiny兄,偶也更新了一下,你去看看吧:
http://blog.csdn.net/dh20156/archive/2006/12/21/1451694.aspx
发表评论  


当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
Csdn Blog version 3.1a
Copyright © never-online