Caching jquery selector results using memoize underline

I am looking into ways to optimize my single page application and I am currently focusing on jQuery selectors.

I'm not a javascript expert, but I understand that it is much more efficient for storing the jquery selector results in a reusable variable as opposed to re-querying the dom.

So, for example, instead of this:

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

      

It makes sense to do this:

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

      

Also, I understand that there can be big performance benefits when using a find () selector attached to an existing jQuery object to reduce the number of requests:

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

      

I'm interested in exploring the possibility of storing many of the selectors that we constantly use in our application, not only in a local variable, but perhaps in a hash or somewhere external. I don't know if this will be optimal, but I'm curious what kind of answers it gets.

I tried (unsuccessfully) to use the memoize function of Underscore.js to accomplish this, but it doesn't work as I expected. But the concept would look like this:

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

      

The idea is that the actual work of the "return $ (selection)" result is executed once, after which the result is cached and returned.

I would like to use it like this:

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

      

In this simple example, the key to the cache is ".highlighted" and the utility knows how to access the result, thereby saving the work of jumping the DOM out of re-execution. Currently my implementation is not working as it shows a "return" request every time.

I'm not sure if this approach is wrong, but I feel like it is possible. However, please let me know if you see a way for this to work, or if you see a better way to do it.

+3


source to share


5 answers


It's not a bad idea, and in fact you can already find dozens of similar memoization modules for JQuery already. The main problem with your approach is knowing when you can memoized the result and when it is invalidated due to changes in the DOM. Since jQuery code is just as likely to add, remove, and modify nodes in the DOM, since they have to choose from it, knowing when you should invalidate a selector from your memoized cache, this is a non-trivial problem.

The way memoize plugins work around this is usually to provide some alternative syntax, such as $_()

for selectors that you want to keep in memory and / or from which you want memoized results. However, this adds an extra string / hash lookup to the selector. So in the end, what is the true advantage of this caching locally?

Generally speaking, it's best to learn more about how jQuery is essentially a DSL providing monadic conversions to the DOM (also known as chaining).

For example, your code in the question could be written:

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

      



You can even use .end()

to push the selector stack, since the chains are already remembered in the results:

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

      

In addition, most jQuery options can be overpriced. Identifiers (selectors starting with #

) are passed to swift DOM methods, for example document.getElementById

, not to the Sizzle engine. Repeating calls for selectors that resolve a single DOM element eg. $(this)

do not really matter for speed.

If speed is indeed an issue in the looping loop of your code, it is almost certainly worth reorganizing this section from using jQuery's picker. In my tests, you can get a 10-50% performance boost from caching a local selector depending on the complexity of the selector and a 1000% performance boost from native DOM selectors using jQuery selectors.

+2


source


I can talk specifically about memoize, but I can say that your strategy of maintaining an associative array of cached jQuery collections is a good idea. With that said, you need to consider a few things:



  • It is not always possible to cache a selector that will only be used 0 or 1 times up front.
  • If you are trying to compute something dynamic about a set of elements (such as length), you will need to reselect / re-cache the element before getting the value. For example:

    var $ div = $ ("div");

    $ div.length; // say 1

    // ... some code that adds some divs to the page

    $ div.length; // 1 more!

  • I would consider doing the caching "in time". In other words, only cache the element the first time you need it.

0


source


The general idea is good. As others have said, you have to be careful not to cache things that need to be requested.

The reason for the failed attempt is because you are using it incorrectly _.memoize

. It will look like this:

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

      

memoize

returns the function that you then called. It essentially creates a wrapper around your function that handles caching.

0


source


I would say It makes sense to do the following:

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

      

about LOB storage, if you want to reuse it throughout your session, I looked into it and found that the local store allows a simple static object, so it won't accept myapp. $ result, for example, I gave up on this idea.

0


source


Caching is the best practice in jquery.

I have divided caching into two categories.

1.local cahching : scope inside some function.

ex:

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

      

Points to keep in mind when using:

I am. for caching items that are accessed by very few functions or speak only about that function.

II. When items are added dynamically than you need to re-initialize each time, local caching is better.

III. keep all var declarations at the top along with the variables that are used for caching.

IV. cache once and then filter with .filter('selector')

or .filter(function)

ex.elm.filter(':checked').dosomething();

2. Global caching Whose scope applies to all functions

for global caching, declares an object and does all caching as a pair of key values ​​in this variable.

Example:

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

      

// remeber will always add the cahing variable inside the document ready function if you declare a global variable at the top of the page. Since the elements will not be present in the dom, therefore the selectors will return undefined.

if you put all scripts on the page I prefer than you can use

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

      

indicates memorization

  • Wrapper items are best cached in global caching. As children, you can find them using .find () and are generally practically unchanged. (I suggest keeping the entire wrapper element with IDs, since ID is the fastest way to select.)
  • Use only for frequently used items. don't use it for a selector for which the element will be added later.
  • Create a local reference to your global object inside the function, since accessing global things is more expensive than accessing local things. ex: function some () {var elm = global.sel1; // now doing something with the elms}

Setter and getter function for global caching

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

//to use it
var elm=getGlobal('selector');

      

-1


source







All Articles