Uncommon JavaScript Idioms That Should Be Common

filed under javascript

There are a lot of anti-patterns being used in JavaScript. In fact, the entire language as it's used by most people is pretty much the biggest anti-pattern in recorded history. But that doesn't mean it's a bad language, just misunderstood. I haven't been programming in JavaScript for as long as some people, but here are a few idioms and small design patterns I've picked up that make your code more maintainable and well-structured.

The Correct Way To Write a JQuery plugin

The JQuery documentation has a recommended way of writing a JQuery plugin that is just wrong.

They recommend you use a massive object wrapped in a closure, and make the JQuery plugin binding a dispatcher to this object. That is, put bluntly, bullshit. You know why? You can't write automated tests for that. Any decent sized project needs automated testing, and JavaScript is no exception.

You know who does know how to write a JQuery plugin? The Bootstrap developers. Well, nearly. They create an actual class, give it a constructor and some methods, then set the JQuery dispatcher method to be the constructor of the object. The only problem with this? It's still in a damned closure. No automated testing for that, unless you want to write some complicated build step that dumps the unit tests and target script into the same closure, but that's some flimsy shit.

Yeah yeah, I understand what closures are for, and they're very useful in most cases, but not when you're writing a plugin. A friend of mine has got an entire setup devoted to writing extensible JQuery plugins, and you know what's wrong with that? The fact that he had to do it at all. You can't extend JQuery plugins, you're supposed to wrap them again and again, or modify the code completely. Good bye, code reuse. If you want to use a plugin in a slightly different way on a particular page, you get to rewrite a lot of functionality in the plugin to allow both cases, giving different configuration, different code paths. It's bullshit. Why does no one think this is a problem? You should be extending the plugin into a child plugin, and then writing the new features you want in there.

The other problem with the recommended JQuery plugin structure is that they butcher the ever living fuck out of the concept of "separation of concerns". Want to separate DOM interaction from regular function calls? Nope, don't do that! Every plugin I've ever seen for JQuery starts manipulating the DOM in some otherwise innoculous function. You want to see how to write a proper JQuery plugin? Here you go.

ModalDialog = {
    defaultSettings: function() {
        return {
            title: '',
            message: '',
            buttons: [
                Button('OK', function(dialog) {
                    console.log('OK clicked');
                }),
                Button('Cancel', function(dialog) {
                    console.log('Cancel clicked');
                }),
            ]
        }
    },

    constructor: function(settings) {
        this.settings = $.extend(ModalDialog.defaultSettings, settings);

        $(this).html('foo');
    },

    show: function() {
        // render the html, and NOTHING MORE.
        $(this).show();
    },

    hide: function() {
        // render the html, and NOTHING MORE.
        $('#weeee').hide();
    },
}

The structure isn't perfect and I doubt it will run, but that's a lot better than wrapping everything in closures and providing a single entry point for multiple methods. You should have JavaScript object representations of every plugin on your website. It makes code easier to understand, rather than an abstract selector and this.

The only things in that plugin that should, nay, will interact with the DOM, are the show and hide methods. Want to extend this plugin? Use Object.prototype to extend it. Create a way to easily access parent attributes and methods. If a plugin needs to do multiple modifications of the DOM, it's a sign that the plugin is probably too complicated and should be broken up somehow. There are exceptions though for this though, of course, like a calendar widget or something, but I'm not talking about those. I'm talking about star rating plugins, or custom context menus.

As an example of how terrible the JQuery plugin situation is, at work, we're using Bootstrap's modal dialog plugin for, well, dialog boxes. We wanted to add some stuff for some pages, we were forced to write a wrapper around it. Then, on some pages, we wanted to have a modal wizard popup. We got write another wrapper around our original wrapper now, rewriting the original wrapper in a large enough way that it could be wrapped easily. We've gone through three iterations of a modal plugin now, when we should have just been able to extend it in the first place. That's crap.

How to handle Ajax requests

Always send your responses back as JSON. Always. If you need HTML, create a JSON object that has a single attribute, html. The day will come when you have to extend that piece of code to return more than just HTML, which will mean sending... JSON. So cut to the chase, and send JSON from the very beginning. Abstract it, and make it a core part of your framework. Don't complain about the bandwidth, it adds another 9 bytes to the response. Just do it.

If you don't always return JSON, then you know what happens? Every time you do an Ajax request, you get to write the handler manually, yourself. You can start out with nice, JQuery idioms like so;

$('myelement').html('loading').load('/somecoolhtml.html')

But when you eventually need JSON, it comes out to this:

$.get('/somecoolurl/', function(data) {
    $('myelement').html(data.html);

    // do something else with data...
});

It looks uglier, but it's more predictable. In-fact, you shouldn't even have to write this kind of code all the time either. You should have written something like that maybe once in the entire project. You should have a framework for doing Ajax requests, so you can write as little code as possible to handle it. Want an example? Check out Facebook's JavaScript on Ajax responses. That is some insane, crazy shit. The response structure for Ajax requests is basically like this:

{
    "jsmods": [
        // target selector            // action  // content
        ["#Some .wonderous selector", "prepend", "<p>Hello friend</p>"],
        ["#Some .wonderous selector", "replace", "<p>oh no that poor chicken</p>"],
    ]
}

There's a lot more to it than that, in Facebook's case. In fact, it's really complex. But that's beside the point. The point is it's extendable, abstracted and concise. You basically have a single, global Ajax callback handler for everything on your website when you have that much structure. You have a handler that takes a list of actions, and applies them. Tada, instant maintainability.

The downside to this is that a lot of purists will claim you shouldn't have view specific code in interwoven with your controllers, i.e. specifying the selectors in the view functions is crap, because the view function shouldn't care about details like that. I agree, it is crap, but I can't think of an alternative. Possibly you could do it for the request object, but then you've got half-and-half data all over the place, which is just as bad, if not worse.

So basically, you've got two options here;

  • Keep doing everything the way it's done now, and write the same tiny snippets over and over again, but you don't intertwine views and controllers.
  • Write a central repository for handling responses, and be mindful of the fact that you'll have JQuery selectors in your controllers from that moment on.

I suppose there's a third option, though. Instead of writing nice code like this, which you get from the server-side method:

$.get('/ajax/');

You could instead write something like this:

Util.Ajax.get('/ajax/', $('#mytargetselector'));

Where Util.Ajax.get would take the url and any necessary data, as well as the selector, and apply it itself. This doesn't really fix the problem though - if the actions sent back from the server need to operate outside the bounds of the selected object, you're screwed. It also means you've got selectors both in your client-side Ajax calls, and the server-side responses, which is arguably even worse.

So basically, this option is a good idea, if you're willing to take a hit on some impure code. But I'm sure you've never written code like that before, right?

If you think of a pure solution that provides similar structure, let me know. I'd love to hear it.

I've got an example of this sort of code in action in a bitbucket project. Note that it's the first version I mentioned, where all modifications happen at the document level, not relative to a particular object. It wouldn't be too much work to add it, though.

Actually design your JavaScript framework

I keep seeing so many articles on how large JavaScript projects are unmaintainable, and you know what? They're right, in most cases, for one reason: No one applies any sort of design or thought into it. JavaScript projects are just a folder full of plugins and page level scripts, and that is fucked. We've got how many years of software engineering behind us, and that's what we're producing? Here's a crap project structure:

/static/
    /js/
        /plugins/
            mygreatplugin.js
            jquery.randompluginfrom2004.js
        index.js
        common.js /* contains every function written since 1998 */
        login.js
        utils.js /* contains functions from common.js with different casing and no comments */
    /css/

And it also happens to be 95% of project structures, ever made. You can't really call a language-specific project unmaintainable when the only reasons for being unmaintainable are sloppy coding and poor design decisions, the same way you can't call a specific brand of hammer crap because poor builders will use them to make shit. The biggest problem with JavaScript is that its all a giant bucket, and you have no explicit namespaces. How do you get around that? Create global objects that act as namespaces. Here's a slightly less shitty version:

/static/
    /js/
        core.js
        /external/
            /jquery.js
            /jquery.randompluginfrom2004.js
        /utils/
            /string.js
            /regexp.js
        /ui/
            /dialog.js
            /contextmenu.js

Where core.js implements this:

window.Utils = {}
window.Ui = {}

And no more. By decree, core.js will never have more than those bare, basic object definitions. If you put anything else in core.js, your head will catch fire, and Ted Bundy will breed with that thing from The Mist and come and get you. Each child script in the utils directory extends the Utils object with more functions or objects. If a child script pollutes the global namespace with crap, Tentacle Bundy will get you. That's all it takes to regain some sanity in your JavaScript projects and make them more maintainable.

If you don't like the verbosity this sort of thing creates, for example;

/* repeated for 25 lines */
var domain = Utils.regexp.validDomain(possibleDomain);

Then temporarily copy the function out of the namespace, like you'd do in any other language;

var validDomain = Utils.regexp.validDomain;

/* repeat again, but with less verbosity this time */
var domain = validDomain(possibleDomain);

Conclusions

So there you have it, some more JavaScript design patterns that make your code base more maintainable. I'll probably be adding more to this in the future as separate articles, as there's no shortage of ways to fix up JavaScript.

Related Posts