jQuery与DOM性能比较

4

我编写了这段代码来比较jQuery和DOM的性能。每个浏览器的表现都不同,最差的表现者是Firefox,运行jQuery时速度慢25倍。 这是预期的行为吗?我没想到使用jQuery会有如此大的影响。

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>
<script  type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script language="JavaScript" type="text/javascript">
$(function () {
    var i=0;
    var dtb = new Date();
    while(i < 1000000)
    {
        var index = Math.floor(Math.random()*30);
        i++;
        var elem = document.getElementById('d'+index);
    }
    var dte = new Date();
    alert(dte-dtb);

    i=0;
    var dtb2 = new Date();
    var body = document.getElementById('cog');
    while(i < 1000000)
    {
        var index = Math.floor(Math.random()*30);
        i++;
        var elem = body.childNodes[index];
    }
    var dte2 = new Date();
    alert(dte2-dtb2);

    i=0;
    var dtb3 = new Date();
    while(i < 1000000)
    {
        var index = Math.floor(Math.random()*30);
        i++;
        var $elem = $("#d"+index);
    }
    var dte3 = new Date();
    alert(dte3-dtb3);



    /////EDIT//////
    ///// Implemented an Array as suggested by Erik Reppen  ////////


    i = 0;
    var idNames=new Array(30);
    while(i<30){
        idNames[i] = $("#d"+i);
        i++;
    }


    i=0;
    var dtb4 = new Date();
    while(i < 1000000)
    {
        var index = Math.floor(Math.random()*30);
        i++;
        var $elem = idNames[index];
    }
    var dte4 = new Date();
    alert(dte4-dtb4);

    /////EDIT//////////////////////////////////////////////



});

</script>
</head>

<body id="cog">
<div id="d0">sdfkjjfgdfd@@@</div><div id="d1">sdffgdfd@@@</div><div id="d2">sddfgfd</div><div id="d3">sdasfd</div><div id="d4">swqedfd</div><div id="d5">sddfdsfd</div><div id="d6">sdfd</div><div id="d7">sdsdffd</div><div id="d8">sdfsdfd</div><div id="d9">sdfkjlkjd</div><div id="d10">sdm ,nfd</div><div id="d11">sdcxvfd</div><div id="d12">sdxzcmfd</div><div id="d13">shgjmdfd</div><div id="d14">sdfvcbd</div><div id="d15">sdf;k;d</div><div id="d16">sdjklfd</div><div id="d17">sd412fd</div><div id="d18">sdfkyhkd</div><div id="d19">sdasdfd</div><div id="d20">sdhdfgsfd</div><div id="d21">sdfdsad</div><div id="d22">sdasdfd</div><div id="d23">sddfgdffd</div><div id="d24">sdklugiffd</div><div id="d25">sddfsafd</div><div id="d26">sdfq21fd</div><div id="d27">42324sdfd</div><div id="d28">sdnhmjkgufksfd</div><div id="d29">sdqwefdLAST</div>
</body>
</html>

2
请注意:jQuery本身正在进行DOM操作。 - BeemerGuy
1
jQuery不仅仅是DOM操作,当有人只期望进行DOM操作时,这可能会影响性能。作者应检查自己的基准测试是否与其他常见测试工具(如http://jsperf.com/)相比具有价值。 - MaxArt
1
我会更多地缓存操作,因为这些是非常大的循环。在第一个循环之前将所有ID缓存到数组中,以便您的循环只是命中数组,而不是在访问getElementById之前连接字符串。然后在下一个循环之前将body.childNodes缓存在变量中。特别是IE在涉及构建/拆除过程的DOM访问方面非常慢。对于JQ,我会执行类似于childNodes的循环,但是缓存$('body').children(),然后在循环内部仅使用cachedChildren[index](除非您宁愿拥有每个子元素的JQuery实例而不是常规dom对象)。 - Erik Reppen
1
算了,我在我的答案中添加了一些片段。 - Erik Reppen
4个回答

12

这个:

$('#someId');

最终归结为JQuery函数的表达式

document.getElementId('someId'); // and then wrap it in a JQuery object and return it

但首先,它必须执行一堆逻辑来根据您发送的参数确定您的意图。类似于(我知道这不仅仅是这个):

它是一个字符串吗?是的。有空格吗?没有。它以“#”、“。”或某个有效的标签名开头吗?它以“#”开头。太好了,只需按ID获取,打包并返回它。

现在尝试进行此测试:

$('#someId.active > .someClass:visible')

当你尝试在IE7中编写DOM时,你会发现JQuery的全部优点。

总体来说,反复选择DOM元素是一件有些愚蠢的事情,不管你使用核心DOM方法还是JQuery。这就像在循环中没有使用函数却抱怨函数调用开销一样。尝试在缓存初始元素获取后比较一些DOM方法和JQ的等效方法。JQ可能仍然比较慢,但我怀疑它不会慢到25倍以上。

var $_someId = $('#someId');
dom_someId = document.getElementById('someId');
//now try looping a JQuery method vs an equivalent set of DOM methods for each

===与原始测试无关,但有所帮助===

以下是根据下方评论以及您提出的问题而列出的一些示例,这些是在循环之前需要完成的事项。

//caching ID names before loop
var i = 30,
idNames = [];
while(i--){ //confusing but tests as i, then inside i is i-=1
idNames[i] = 'd'+(i+1);
}
注意:在循环中随机生成索引后,您将数组索引设置为0-30,因此请删除+1。实际上,我不确定为什么1-31不会炸毁childNodes循环,因为它从未触及第一个元素,并且应该尝试访问不存在的两个元素。删除+1后,它会选择0-30。上面的循环假定您想要1-31,但我刚才看到HTML只有30个并且从1开始。
//caching object/property lookup and DOM Access/HTMLCollection/obj instantiation
var bodyChildren = body.childNodes; //DOM object lookups cost performance

//caching JQ so you can use the exact same loop afterwards
var bodyChildren = $('body').children();

//inside loops
bodyChildren[index];

1
谢谢您的回答。我实际上正在比较DOM getElementById和childNodes,但后来决定将两者与jQuery进行比较,并对结果感到惊讶,于是发布了这个问题。DOM选择测试实际上对我来说很重要,因为应用程序将不得不执行相同的操作,而且应该是超快的。我已经决定使用getElementById,这主要是由于IE在childNodes方面太慢了。 - Registered User
1
为了更公平地比较getElementById和childNodes,您可能希望为所有这些ID构建查找表,因为在典型情况下,您将传递一个字符串而不是字符串连接步骤。每次进行属性查找时,特别是与DOM相关的查找,都会有一个过程,因此在循环之前将body.childNodes分配给一个变量。 - Erik Reppen
谢谢Erik!我已经更新了原帖并包含了你建议的数组解决方案,同时也更改了采纳的答案,因为你提供了一个实际的解决方案。再次感谢你的支持! - Registered User

5
jQuery是一种包装器,可以在每个主要浏览器中以一致的方式规范化DOM操作。它的执行速度比直接DOM操作慢25倍,这是完全合理的。性能损失是为了具有简洁的代码而进行的权衡。
总的来说,JavaScript是一种高度异步的语言。它的大部分使用涉及等待用户触发的回调或计时器。因为有很多时间,性能几乎从来不是一个问题。用户不会注意到一个运行时间为1ms与一个运行时间为25ms之间的过程的差异。
如果您的脚本中确实存在显着的性能损失,请使用工具分析代码花费最长的时间的地方。
毕竟,过早的优化是万恶之源。

我曾认为过度优化的热爱才是万恶之源... - nnnnnn
我正在学习DOM,得到了一个建议,为了尽快找到前端的第一份工作,最好多研究一下jQuery。 - muhammad tayyab

4
这是因为$("#d"+index);document.getElementById('d'+index);的功能不同。后者获取一个DOM对象,这是一种内置于浏览器中的本地对象。
然而,$创建了一个jQuery对象。首先,它必须解析选择器,因为jQuery可以通过类、属性、祖先等方式查找元素,而document.getElementById只能通过ID查找元素。jQuery对象不是本地对象,因此创建速度较慢,但它也有更多的潜力。例如,jQuery对象(实际上是本地DOM对象的包装器)提供了next, val, bindon方法。使用jQuery来进行选择(或至少使用这样简单的选择)比使用本地DOM对象会更慢,但是它使编程变得更容易,通常也会使执行更快,因为jQuery的作者可能比你更擅长Javascript。

1
你现在用jQuery就像是用大锤子去杀一只蚊子。每次你写 $("#d"+index),你都在让jQuery解析你的选择器,将选择器应用到DOM上,然后将结果包装成一个占用内存的对象。这样做非常耗时。
建议:只有在需要使用jQuery且性能不是函数的主题时才使用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接