Tuesday, April 17, 2012

jQuery code organization and performance


After doing some research on the subject, I've been experimenting a lot with patterns to organize my jQuery code (Rebecca Murphy did a presentation on this at the jQuery Conference for example).



Yesterday I checked the (revealing) module pattern. The outcome looks a bit reminiscent of the YUI syntax I think:




//global namespace MyNameSpace
if(typeof MNS=="undefined"||!MNS){var MNS={};}

//obfuscate module, just serving as a very simple example
MNS.obfuscate = function(){
//function to create an email address from obfuscated '@'
var email = function(){
$('span.email').each(function(){
var emailAddress = $(this).html().replace(' [ @ ] ','@');
$(this).html('<a href="mailto:' + emailAddress + '">' + emailAddress + '</a>');

});
};
return {
email: email
};
}();

//using the module when the dom's ready
jQuery(document).ready(function($){
MNS.obfuscate.email();
});



In the end I had several modules. Some naturally included "private members", which in this case means variables and/or functions which were only of importance for other functions within this module, and thus didn't end up in the return statement.



I thought having connected parts of my code (everything which has to do with the search for example) combined in a module makes sense, gives the whole thing structure.



But after writing this, I read an article by John (Resig), where he also writes about the performance of the module pattern:



"Instantiating a function with a bunch of prototype properties is very, very, fast. It completely blows the Module pattern, and similar, out of the water. Thus, if you have a frequently-accessed function (returning an object) that you want people to interact with, then it's to your advantage to have the object properties be in the prototype chain and instantiate it. Here it is, in code:




// Very fast
function User(){}
User.prototype = { /* Lots of properties ... */ };

// Very slow
function User(){
return { /* Lots of properties */ };
}



(John mentions he is not against the module pattern "per se" though - just to let you know :)



Then I wasn't sure anymore if I was going into the right direction with my code. The thing is: I don't really need any private members, and I also don't think I need inheritance for the time being. All I want for now is a readable/maintainable pattern. I guess this boils down to personal preference to a certain extend, but I don't wanna end up with readable code which has (rather serious) performance issues.



I'm no JavaScript expert and thus even less of an expert when it comes to performance testing. So first, I don't really know in how far the things John mentioned ("frequently-accessed function (returning an object) that you want people to interact with", lots of properties, etc.) apply to my code. The documents my code interacts with are not huge, with 100s or 1000s of elements. So maybe it's not an issue at all.



But one thing which came to my mind is that instead of just having




$('span.email').each(function(){
var emailAddress = $(this).html().replace(' [ @ ] ','@');
$(this).html('<a href="mailto:' + emailAddress + '">' + emailAddress + '</a>');
});



(inside the domready function), I create two "extra" functions, obfuscate and email, due to the use of the module pattern. The creation of additional functions costs some time. The question is: will it be measureable in my case?



I'm not sure if a closure is created in my example above (in an interesting post on the jQuery forum I read the following: "There is a philisophical debate on whether an inner fuction creates a closure if it doesn't reference anything on an outer function's variable object..."), but I did have closures in my real code. And even though I don't think I had any circular references in there, I don't know in how far this could lead to high(er) memory consumption/problems with garbage collection.



I'd really like to hear your input on this, and maybe see some examples of your code. Also, which tools do you prefer to get information on execution time, memory and CPU usage?


Source: Tips4all

2 comments:

  1. I also don't think I need inheritance for the time being


    Indeed. This doesn't really apply to using modules as a namespace. It's about class instance analogues.

    Objects you create by making every instance from a completely new {name: member} object are less efficient than objects you create using new Class with Class.prototype.name= member. In the prototype case the member value is shared, resulting in lighter-weight instances.

    In your example MNS is a singleton, so there is no benefit to be had by sharing members through a prototype.


    I'm not sure if a closure is created in my example above


    Yes, it is. Even when no variables are defined in the outer function, there is still a LexicalEnvironment and scope object created for the outer function, with this and arguments bound in it. A clever JS engine might be able to optimise it away, since every inner function must hide this and arguments with their own copies, but I'm not sure that any of the current JS implementations actually do that.

    The performance difference, in any case, should be undetectable, since you aren't putting anything significant in the arguments. For a simple module pattern I don't think there's any harm.


    Also, which tools do you prefer to get information on execution time, memory and CPU usage?


    The place to start is simply to execute the code 10000 times in a for-loop and see how much bigger new Date().getTime() has got, executed several times on as many different browsers as you can get hold of.


    $(this).html().replace(' [ @ ] ','@');


    (What is this supposed to do? At the moment it will read the HTML of the span into a new String, replace only the first instance of [ @ ] with @, and return a new String value. It won't change the existing content in the DOM.)

    ReplyDelete
  2. How much Javascript do you have? In my experience, on sites with lots of Javascript code on the pages, performance problems generally come from code that actually does things. Generally, problems stem from trying to do too many things, or trying to do some particular thing really badly. An example would be trying to do stuff like bind handlers to elements in table rows (big tables) instead of using something like "live".

    My point is that things like how your modules or functions or whatever get organized is almost certainly not going to pose any sort of real performance issue on your pages. What is motivating you to go to all this trouble?

    ReplyDelete