【问题标题】:jQuery object: to cache or not to cache?jQuery 对象:缓存还是不缓存?
【发布时间】:2012-08-04 16:30:37
【问题描述】:

我的 Javascript (JS) 代码带来了一些麻烦,因为有时我需要在同一个函数中多次访问同一个 DOM 元素。还提供了一些推理here

从性能的角度来看,是创建一次jQuery对象然后缓存它更好还是随意创建相同的jQuery对象更好? 示例:

function(){
  $('selector XXX').doSomething(); //first call
  $('selector XXX').doSomething(); //second call
  ...
  $('selector XXX').doSomething(); // n-th call
}

function(){
  var  obj = $('selector XXX');
  obj.doSomething(); //first call
  obj.doSomething(); //second call
  ...
  obj.doSomething(); // n-th call       
}

我认为答案可能取决于“n”的值,因此假设 n 是一个“小”数(例如 3),然后是一个中等数(例如 10),最后是一个大数(例如 30,就像对象在 for 循环中用于比较)。

提前致谢。

【问题讨论】:

  • 假设选择器的结果在代码执行期间不会发生变化,那么完全没有理由缓存选择器。
  • 这个答案与其他答案非常不同。你是唯一不建议缓存的人。您如何看待其他答案?
  • 我认为他和我们的意思是一样的——他说“没有理由不缓存”——双重否定=>我们应该缓存:)。干杯!

标签: jquery caching


【解决方案1】:

缓存元素总是更好,如果 n 大于 1,缓存元素,或者将操作链接在一起(你可以为 大多数$('#something').something().somethingelse(); > jQuery 操作,因为它们通常返回包装集本身)。顺便说一句,以金钱符号$ 开头的缓存变量名称已成为一种标准,因此在后面的代码中,很明显您正在对 jQuery 集执行操作。所以你会看到很多人都在var $content = $('#content'); 然后$content.find('...');

【讨论】:

  • 问题开始于我通常用Java开发的事实,其中“缓存”对象意味着保留“大量”内存。所以,我想知道性能和副作用内存消耗。例如,如果我将document 存储在var 中,它会消耗大量内存吗?
  • @JeanValjean 如果你真的做var x = document,你分配的内存不会超过对象引用所需的内存。 document 访问的对象是单例。此外,var x = jQuery(document) 返回类似于 jQuery 对象的内容,其成员是一个仅包含 document 的数组,这比普通对象引用几乎不贵(事实上,jQuery 本身可能将 jQuery(document) 处理为一个特殊情况开始)。要让 jQuery 对象使用“大量”内存,请使其匹配许多元素:jQuery('*')
【解决方案2】:

第二个更好。最重要的是,它更干净。以后,如果你想改变你的选择器,你只需要改变一个地方。否则你需要在N个地方改变它。

其次,它应该表现得更好,尽管用户只会注意到特别重的 dom,或者如果你经常调用该函数。

【讨论】:

    【解决方案3】:

    如果换个角度看这个问题,正确答案是显而易见的。

    在第一种情况下,您在其出现的每个位置复制选择逻辑。如果更改元素的名称,则必须更改每个出现的位置。这应该是足够的理由不这样做。现在你有两个选择——要么缓存元素的选择器,要么缓存元素本身。将元素用作对象比使用名称更有意义。

    在性能方面,我认为效果可以忽略不计。可能您将能够找到此特定用例的测试结果:缓存 jQuery 对象与总是重新选择它们。如果您有一个大型 DOM 并进行大量查找,性能可能会成为一个问题,但您需要自己查看是否是这种情况。

    如果您想准确查看对象占用了多少内存,可以使用Chrome Heap Profiler 并在那里查看。我不知道其他浏览器是否可以使用类似的工具,并且可能实现的性能会有很大差异,尤其是在 IE 的情况下,但它可能会满足您的好奇心。

    IMO,您应该使用第二种变体,将选择的结果存储在一个对象中,与其说是为了提高性能,不如说是尽可能少地重复逻辑。

    关于缓存$(this),我同意Nick Craver's answer。正如他在那里所说,您还应该尽可能使用链接 - 清理您的代码并解决您的问题。

    【讨论】:

    • 您的编辑回答了我向 Paolo Bergantino 提出的问题。谢谢
    • 你认为对$(this)的调用可能会重复还是应该缓存它?
    • 嗯!对此我不确定!可能$(this) 始终可用,不能使用选择器检索它。因此,访问缓存变量或访问$(this) 的成本应该是相同的!不是吗?在这种情况下,缓存$(this) 并不能提高性能(也许你可以节省一些代码)。
    • @jonny_cage:jQuery 要做的第一个检查是查看传递的选择器是否是 DOMElement 对象,如果是,则简单地返回包装的元素。我不介意人们缓存this,因为养成缓存选择器的习惯是件好事,但对于this,你选择哪一个就无关紧要了。
    【解决方案4】:
    【解决方案5】:

    我几乎总是更喜欢缓存 jQuery 对象,但是根据您用于选择器的具体内容,其好处会有很大差异。如果您使用 ids,那么好处远小于使用选择器类型的好处。此外,并非所有选择器都是平等创建的,因此在编写选择器时请记住这一点。

    例如: $('table tr td') 是一个非常糟糕的选择器。尝试使用 context 或 .find() 会有很大的不同。

    我喜欢做的一件事是在我的代码中放置计时器,看看它的效率如何。

    var timer = new Date(); 
    // code here
    console.log('time to complete: ' + (new Date() - timer));
    

    大多数缓存对象将在不到 2 毫秒的时间内执行,而全新的选择器需要更长的时间,因为您首先必须找到元素,然后执行操作。

    【讨论】:

      【解决方案6】:

      在 JavaScript 中,函数通常是短暂的,尤其是在由浏览器托管时。然而,一个函数的作用域可能比函数的寿命更长。例如,当您创建一个闭包时,就会发生这种情况。如果您想防止 jQuery 对象被长时间引用,您可以将 null 分配给在完成该变量后引用它的任何变量,或者使用间接创建闭包。例如:

      var createHandler = function (someClosedOverValue) {
          return function () {
              doSomethingWith(someClosedOverValue);
          };
      }
      
      var blah = function () {
          var myObject = jQuery('blah');
      
          // We want to enable the closure to access 'red' but not keep
          // myObject alive, so use a special createHandler for it:
          var myClosureWithoutAccessToMyObject = createHandler('red');
      
          doSomethingElseWith(myObject, myClosureWithoutAccessToMyObject);
      
          // After this function returns, and assuming doSomethingElseWith() does
          // not itself generate additional references to myObject, myObject
          // will no longer have any references and be elligible for garbage
          // collection.
      }
      

      因为jQuery(selector) 可能最终不得不运行昂贵的算法,甚至对浏览器无法直接处理的复杂表达式稍微遍历 DOM 树,所以最好缓存返回的对象。此外,正如其他人所提到的,为了代码清晰,最好缓存返回的对象以避免多次键入选择器。即,DRY 代码通常比 WET 代码更易于维护。

      然而,每个 jQuery 对象都有一些开销。因此,将大量 jQuery 对象存储在全局变量中可能是一种浪费——除非您确实需要对大量这些对象进行操作并且仍然将它们视为不同的。在这种情况下,您可以通过直接缓存 DOM 元素的数组并使用 jQuery(DOMElement) 构造函数来节省内存,该构造函数在迭代它们时基本上应该是空闲的。

      不过,正如人们所说,您只能通过对不同方法进行基准测试来了解适合您特定情况的最佳方法。即使理论看起来很合理,也很难预测现实;-)。

      【讨论】:

        猜你喜欢
        • 2011-06-15
        • 1970-01-01
        • 2018-03-09
        • 2015-10-12
        • 1970-01-01
        • 2020-06-30
        • 1970-01-01
        • 1970-01-01
        • 2016-09-16
        相关资源
        最近更新 更多