使用下划线memoize缓存jquery选择器结果

时间:2013-02-08 18:01:45

标签: jquery caching underscore.js

我正在研究优化单页应用程序的方法,目前我正专注于jQuery选择器。

我不是javascript专家,但我的理解是将jquery选择器的结果存储在变量中以便重用,而不是重新查询dom,这样做的效率要高得多。

因此,例如,而不是:

$("#myItem").doSomething();
$("#myItem").doSomethingElse();

这样做更有意义:

var result = $("#myItem");
result.doSomething();
result.doSomethingElse();

另外,我的理解是,使用附加到现有jQuery对象的find()选择器可以获得很大的性能优势,以减少必要的查询量:

$("#myItem").find(".highlighted");

我有兴趣探索存储许多选择器的可能性,这些选择器在我们的应用程序中不仅在局部变量中重复使用,而且可能在某个地方使用散列或外部函数。我不知道这是否最佳,但我很好奇收到的回复。

我尝试(不成功)使用Underscore.js的memoize函数来执行此操作,但它没有按照我预期的方式工作。但是,这个概念看起来像这样:

   jquerySelect = function () {
                var elements = _.memoize(function (selection) {
                    return $(selection);
                });
                return elements;
            };

这里的想法是“返回$(选择)”的实际结果工作执行一次,之后结果被缓存并返回。

我希望能够以下列方式使用它:

utils.jquerySelect(".highlighted").find("span")

在这个简单的例子中,缓存的关键是“.highlighted”,实用程序函数知道如何访问结果,从而节省了遍历DOM的工作。我的实现目前不起作用,因为每次都会发出“return”语句。

我不确定这种方法是否存在缺陷,但我感觉可能存在缺陷。但是,如果您看到一种方法可以使用,或者您看到更好的方法,请告诉我。

5 个答案:

答案 0 :(得分:3)

这不是一个坏主意,事实上你已经可以在那里找到几十个类似的JQuery memoization插件。您的方法面临的主要挑战是知道何时可以使用已记忆的结果,以及何时由于DOM的更改而使其无效。由于JQuery代码可以添加,删除和更改DOM中的节点,因为它们可以从中进行选择,因为知道何时必须从memoized缓存中使选择器无效是一个非常重要的问题。

memoize插件解决这个问题的方法通常是提供一些替代语法,例如$_()用于想要记忆的选择器和/或要从中检索记忆结果的选择器。但是,无论如何,这会为选择器添加额外的字符串/哈希查找。那么,最终,本地缓存的真正优势是什么呢?

一般来说,最好更多地了解JQuery本质上是如何为DOM提供monadic转换(也称为链接)。

例如,您可以编写问题中的代码:

$("#myItem").doSomething().doSomethingElse();

您甚至可以使用.end()来弹出选择器堆栈,因为在链中结果已经在内部进行了记忆:

$("#myItem").doSomething().find('a').hide().end().doSomethingElse();
// a in #myItem are hidden, but doSomething() and doSomethingElse() happen to #myItem

此外,大多数JQuery选择的成本可能被高估。 ID选择(以#开头的选择器)被传递给快速DOM方法,如document.getElementById而不是Sizzle引擎。重复调用解析为单个DOM元素的选择器,例如$(this)对速度没有特别的影响。

如果速度确实在代码的紧密循环中引起关注,那么完全使用JQuery选择引擎重构该部分几乎肯定是值得的。在我的测试中,您可以从本地选择器缓存中获得10-50%的性能提升,具体取决于选择器的复杂性,以及通过JQuery选择器从本机DOM选择器获得1000%的性能提升。

答案 1 :(得分:0)

我可以专门讲述memoize,但我可以说你维护缓存jQuery集合的关联数组的策略是个好主意。话虽如此,你需要考虑一些事项:

  1. 缓存一个只能预先使用0或1次的选择器可能并不值得。
  2. 如果您正在尝试计算关于元素集合的动态(例如长度),则需要在获取值之前重新选择/重新缓存元素。例如:

    var $ div = $(“div”);

    $ div.length; //说它是1

    // ...为页面添加一些div的一些代码

    $ div.length; //还是1!

  3. 我会考虑“及时”进行缓存。换句话说,只在第一次需要时缓存项目。

答案 2 :(得分:0)

总体思路是好的。正如其他人已经说过的那样,您需要注意不要缓存特别需要重新查询的内容。

您尝试失败的原因是您错误地使用了_.memoize。它看起来像这样:

var jquerySelect = _.memoize(function (selection) {
                       return $(selection);
                   });

memoize返回一个您随后会调用的函数。它实际上是在你的函数周围创建一个处理缓存的包装器。

答案 3 :(得分:0)

我会说  这样做更有意义:

var myapp={};
myapp.$body=$(document.body);
myapp.$result = myapp.$body.find("#myItem").doSomething().doSomethingElse();
myapp.$result.find("> .whatever")

关于存储大型对象,如果你想在整个会话中重复使用,我一直在研究它和它。发现本地存储仅允许普通静态对象,所以不接受myapp。例如$ result,然后我放弃了这个想法。

答案 4 :(得分:-1)

缓存是jquery中遵循的最佳实践。

我将缓存分为两类。

<强> 1。本地cahching :其范围仅在某个函数内。

前:

function doSome(){
var elm=$('selector');
elm.something1().something2()  .... chain it on one
}

使用时要注意的要点:

我。缓存由极少数函数访问的元素,或仅由该函数说明。

II。当动态添加元素时,每次都需要重新初始化它 本地缓存更好。

III。将所有var声明保留在顶部以及用于缓存的变量。

IV。缓存一次,稍后使用.filter('selector').filter(function)进行过滤 恩。 elm.filter(':checked').dosomething();

<强> 2。全局缓存所有功能的范围

用于全局缓存,声明一个对象,并将所有缓存作为该变量中的键值对。

例如:

var global={};
$(document).ready(function(){
global.sel1=$('selector1');
global.sel2=$('selector2');
});

//如果要在页面顶部声明全局变量,则remeber始终在文档就绪函数中添加cahing变量。由于元素不会出现在dom中,因此选择器将返回undefined。

如果您将所有脚本放在我喜欢的页面上而不是您可以使用

var global={
sel1:$('selector1'),
sel2:$('selector2')
} ;

指出要记住

  1. 包装元素最好在全局缓存中缓存。作为子元素,您可以使用.find()找到并且通常不会实际修改。 (我建议保留所有包含id的包装元素,因为id是最快的选择方式。)
  2. 仅用于常用元素。不要将它用于选择器,以便稍后添加元素。
  3. 在函数内创建全局对象的本地引用,因为访问全局事物比访问本地事物更昂贵。 ex:function some(){ var elm = global.sel1; //现在用榆树做点什么 }
  4. 全局追逐的setter和getter函数

    function getGlobal(selector){
    //if not present create it .
    if(!global[selector]){
    global[selector]=$(selector);
    }
    return global[selector];
    }
    
    //to use it
    var elm=getGlobal('selector');