【问题标题】:Why is jQuery selector function so slow compared to native DOM methods与原生 DOM 方法相比,为什么 jQuery 选择器函数这么慢
【发布时间】:2013-12-25 01:01:46
【问题描述】:

我知道这个话题已经讨论过好几次了,但我正在寻找更技术和更详细的见解来了解真正发生的事情。

我设计了一系列测试来比较 jQuery 最基本的选择器“#id”和“.class”与各种原生 DOM 方法的速度。

我希望确切地找出结果为何如此。

这里是测试:http://jsperf.com/jqueryspeed

值得注意的是,getElementById 显然是最快的。 为了比较,我添加了 jQuery('#id') 和 jQuery.fn.init('#id') 作为测试,两者之间的区别在于第一个确实实例化了一个全新的 jQuery 对象,而第二个只运行原型函数,因此速度更快。所以,这两者之间的区别是可以理解的。

然而,我不明白的主要区别是 getElementById 的速度和 jQuery.fn.init 的速度之间的巨大差距,它有一个简单的测试来处理一个简单的 ('#id') 请求具体方式,回退到调用 getElementById 本身。

那么,为什么例如在 Chrome 上,这种方法比原生方法慢 8 倍,即使它基本上只是它的包装器?

它也比包装好的 getElementById $(document.getElementById('#id')) 慢大约 3-4 倍...

有什么想法吗?

【问题讨论】:

  • 本机方法经过高度优化,$.fn.init 不仅仅是document.getElementById 的“包装器”。那么为什么会有速度差异让您感到惊讶呢?
  • 它不仅仅是一个包装器,而且它有一个特殊的测试分支,导致最后简单地调用 getElementById。我对有开销并不感到惊讶,但开销如此之大!
  • getElementById 非常快(尤其是在现代浏览器中)。因此,无论有多少开销,都将是$.fn.inittiny 部分。如果您浏览代码,您将看到需要执行的操作。显然,这些操作没有得到优化和/或本质上是较慢的任务。与“轻任务加上其他较重的任务”相比,它是“轻任务”。
  • 仅供参考,我添加了几个测试。一个只是getElementById 的薄包装。另一种是带有几个安全检查的薄包装纸。它们几乎和直接调用本机方法一样快。 jsperf.com/jqueryspeed/3

标签: javascript jquery performance dom getelementbyid


【解决方案1】:

这是当我们使用一个简单的 $('selector')

时 jquery 所经历的代码量

http://james.padolsey.com/jquery/#v=1.10.2&fn=init

如您所见,已完成大量验证、正则表达式匹配、跨浏览器技巧等。

重要的是要意识到 jquery 是一个基于 javascript 构建的库。Javascript 直接在浏览器上执行。而 jquery 在被浏览器执行之前会处理相当多的 javascript 代码。

我个人更喜欢 jquery。我真的不介意节省那些纳秒。jquery 提供的简单程度是惊人的,本身就是一件艺术品。

【讨论】:

  • 当面对一个 jQuery 网站在 DOM 上打磨时出现一些褪色效果,这让我有时间思考。 “我多么希望这个网站的开发者 为节省这 10^9 纳秒而烦恼”。
  • 它不是一个框架而是一个库。框架定义控制流,库是函数的集合。
  • 如果 jquery 这么臃肿,将它预编译为 javascript 是否有意义?只是一个想法。 $('#myid') 在活动网页上不应比 document.getElementById('myid') 慢。因为它们在客观上是等价的。好的,因为 $('#myid') 还需要调用 $('#myid').get(0) ,但是 document.getElementById('x') 仍然是满嘴的,而且说 @987654322 有点贵@
  • @Adria,当我使用旧版 IE 访问您的网站时,我希望您在使用旧版浏览器时为实现 getElementById 而烦恼。 jquery 做这些检查是有原因的。
【解决方案2】:

jQuery 没有什么能像原生 javascript 那样快,这是有原因的:它努力使您的代码兼容跨浏览器且易于使用。它从大多数方法调用中构建一个 jQuery 对象。在这方面,jQuery 将比所需的最小数据路径慢很多,因为它希望提供随时可用的功能。

让我们比较一下这两个“相似”的调用:

document.getElementById("box"):在比 JavaScript 更低级别执行简单查找操作的本机方法。然后它返回已经加载到内存中的 DOM 元素。这是最快的方法之一。

$('#box'):在这里,jQuery 将首先解析您要求它执行的操作。例如,它将验证它是一个格式良好的选择器,然后尝试识别它是什么类型的选择器。验证完成后,它将尝试获取 ID 为“box”的元素。之后,他将创建一个新的 jQuery 对象,用每个预期的属性填充它,并确保所有浏览器(以及旧浏览器)都获得相同的结果。这包括许多后备和合规性测试。当对象准备好使用时,您将获得 ID 为“box”的元素。不像getElementById()那么简单。当目标元素不需要 jQuery 功能时,许多人更愿意使用 getElementById('box') 而不是 $('#box')


更新 - 2017 年 2 月 15 日:

由于 jQuery >= 2.0 不再支持臭名昭著的 IE 6/7/8,因此不再需要一些兼容性测试,从而使 jQuery 更轻、更快。如果您不需要支持旧版浏览器,则可以使用 jQuery >= 2.0 而不是 1.x 来提高整体性能。

【讨论】:

  • 用户喜欢网站的速度;)这就是为什么我的 jQuery-free 网站在半秒内加载比我的竞争对手更受欢迎,它使用 jQuery 并且需要 5+ 秒加载:3
  • 这是一个非常详细的解释,但有点离题。一旦你调用 $('#box'),jquery 唯一做的就是匹配一个正则表达式,然后测试一个匹配项,然后转到一个特殊的块来处理你只请求一个 ID 的情况。在这个简单的路径中没有任何关于浏览器合规性或其他内容的内容......
  • @NiettheDarkAbsol 客户喜欢 jQuery 之类的东西,因为他们可以花更少的钱来获得更多的功能;)我们都希望每个客户都有无限的预算和良好的业务,但现实是,很多都是在投资网站时冒着巨大的风险。从这个意义上说,开发人员还可以在不超出 [价格] 范围的情况下为项目增加更多价值。对于个人项目或雄心勃勃的坚实客户,香草是至高无上的乐趣。很像直接使用 Win32 API 来制作工具,而不是 gtk/qt/fltk/sdl/etc。 :)
  • "document.getElementById("box"):本机方法迭代 DOM 元素"。它甚至不必。 UA 必须在文档中保留命名元素的 hash-map。所以 getElementById 不会迭代,它只是一个查找。 related
  • @Kaiido 我刚刚更新,谢谢。旧答案,我可能会仔细检查整个事情。
【解决方案3】:

我为 jQuery.fn.init(document.getElementById('id')) 添加了另一个测试用例,它比大多数其他方法更快,因为它既不解析字符串也不创建新的 jQuery 对象(它比 getElementById、jsperf 落后大约 50%),当我看到在jQuery.fn.init调用期间执行的jquery代码的来源:

function (selector, context, rootjQuery){
  if (selector.nodeType) {
          this.context = this[0] = selector;
          this.length = 1;
          return this;
  }
}

我只能得出结论,Chrome 和 Firefox 的工程师在优化原生 DOM 操作方面做得非常好。

【讨论】:

    猜你喜欢
    • 2012-05-25
    • 2011-01-18
    • 2012-05-05
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多