How can I write reusable Javascript?

aw crud picture aw crud · Apr 9, 2010 · Viewed 21.2k times · Source

I've started to wrap my functions inside of Objects, e.g.:

var Search = {
  carSearch: function(color) {
  },
  peopleSearch: function(name) {
  },
  ...
}

This helps a lot with readability, but I continue to have issues with reusabilty. To be more specific, the difficulty is in two areas:

  1. Receiving parameters. A lot of times I will have a search screen with multiple input fields and a button that calls the javascript search function. I have to either put a bunch of code in the onclick of the button to retrieve and then martial the values from the input fields into the function call, or I have to hardcode the HTML input field names/IDs so that I can subsequently retrieve them with Javascript. The solution I've settled on for this is to pass the field names/IDs into the function, which it then uses to retrieve the values from the input fields. This is simple but really seems improper.

  2. Returning values. The effect of most Javascript calls tends to be one in which some visual on the screen changes directly, or as a result of another action performed in the call. Reusability is toast when I put these screen-altering effects at the end of a function. For example, after a search is completed I need to display the results on the screen.

How do others handle these issues? Putting my thinking cap on leads me to believe that I need to have an page-specific layer of Javascript between each use in my application and the generic methods I create which are to be used application-wide. Using the previous example, I would have a search button whose onclick calls a myPageSpecificSearchFunction, in which the search field IDs/names are hardcoded, which marshals the parameters and calls the generic search function. The generic function would return data/objects/variables only, and would not directly read from or make any changes to the DOM. The page-specific search function would then receive this data back and alter the DOM appropriately.

Am I on the right path or is there a better pattern to handle the reuse of Javascript objects/methods?

Answer

T.J. Crowder picture T.J. Crowder · Apr 9, 2010

Basic Pattern

In terms of your basic pattern, can I suggest modifying your structure to use the module pattern and named functions:

var Search = (function(){
    var pubs = {};

    pubs.carSearch = carSearch;
    function carSearch(color) {
    }

    pubs.peopleSearch = peopleSearch;
    function peopleSearch(name) {
    }

    return pubs;
})();

Yes, that looks more complicated, but that's partially because there's no helper function involved. Note that now, every function has a name (your previous functions were anonymous; the properties they were bound to had names, but the functions didn't, which has implications in terms of the display of the call stack in debuggers and such). Using the module pattern also gives you the ability to have completely private functions that only the functions within your Search object can access. (Just declare the functions within the big anonymous function and don't add them to pubs.) More on my rationale for that (with advantages and disadvantages, and why you can't combine the function declaration and property assignment) here.

Retrieving Parameters

One of the functions I really, really like from Prototype is the Form#serialize function, which walks through the form elements and builds a plain object with a property for each field based on the field's name. (Prototype's current – 1.6.1 – implementation has an issue where it doesn't preserve the order of the fields, but it's surprising how rarely that's a problem.) It sounds like you would be well-served by such a thing and they're not hard to build; then your business logic is dealing with objects with properties named according to what they're related to, and has no knowledge of the actual form itself.

Returning Values / Mixing UI and Logic

I tend to think of applications as objects and the connections and interactions between them. So I tend to create:

  • Objects representing the business model and such, irrespective of interface (although, of course, the business model is almost certainly partially driven by the interface). Those objects are defined in one place, but used both client- and server-side (yes, I use JavaScript server-side), and designed with serialization (via JSON, in my case) in mind so I can send them back and forth easily.
  • Objects server-side that know how to use those to update the underlying store (since I tend to work on projects with an underlying store), and
  • Objects client-side that know how to use that information to render to the UI.

(I know, hardly original!) I try to keep the store and rendering objects generic so they mostly work by looking at the public properties of the business objects (which is pretty much all of the properties; I don't use the patterns like Crockford's that let you really hide data, I find them too expensive). Pragmatism means sometimes the store or rendering objects just have to know what they're dealing with, specifically, but I do try to keep things generic where I can.