Who Needs Classes Anyway?

12 commentsWritten on March 28th, 2011 by
Categories: JavaScript

I needed to do some client-side formatting of dates that i get back from an AJAX request in JSON format, and to my surprise, JavaScript nor jQuery has something built in for this. I briefly looked around for some jQuery plugins or JavaScript libraries but was quickly lost because there are so many of them and most of them do a lot more than the tiny bit that i need right now. So i figured i'd be better off if i'd just quickly do it myself for now. Granted, i might be better off in the long run using a specific library for this but that's not really the point of this post, so bear with me for a second. What i'd normally do in a situation like this is to think about what i need to solve and create a small class for that which takes care of the job for me. In JavaScript however, there are no classes, just objects and prototypes.

The thing is though: do i really need to think about what kind of class i'll need? Am i not better off thinking "what kind of functionality do i need to implement here?" or "what kind of behavior am i trying to implement?". Does it really matter whether or not the physical packaging of that functionality is a class, an object, a module, or something else? Not really. I just want to provide some behavior, while encapsulating some of the details of how that behavior is implemented.

With a class, that's easy to do because we're all so used to doing that... just create a class with some public methods for the behavior you want to provide and whatever you want to keep private you just mark as private. There, done. So how do you do that in JavaScript? There are actually a bunch of ways to do this in JavaScript, and the code you'll see below is just one example. Some of you will read the code and think "oh ok, you're just emulating private methods here". And while you'd be right in some way, it's a somewhat unfortunate way of thinking about the code because it means you mainly think in terms of classes and objects, whereas it might be more interesting to start thinking more in terms of 'functionality' which can come in many shapes or forms.

Take a look at the code, which i'll discuss further below:

var MyNameSpace = MyNameSpace || {};

MyNameSpace.jsondateformatter = (function () {
    var that = this;

    var toDate = function (jsonDateString) {
        var time = jsonDateString.replace(/\/Date\(([0-9]*)\)\//, '$1');
        var date = new Date();
        date.setTime(time);
        return date;
    };

    var prefixWithZeroIfNecessary = function (number) {
        return number < 10 ? '0' + number : number;
    };

    var dayAsDoubleDigitDayString = function (date) {
        return prefixWithZeroIfNecessary(date.getDate());
    };

    var monthAsDoubleDigitMonthString = function (date) {
        // for some reason, the result of getMonth is zero-based
        return prefixWithZeroIfNecessary(date.getMonth() + 1); 
    };

    var hourAsDoubleDigitHourString = function (date) {
        return prefixWithZeroIfNecessary(date.getHours());
    };

    var minutesAsDoubleDigitMinuteString = function (date) {
        return prefixWithZeroIfNecessary(date.getMinutes());
    }

    return {
        toDate: function (jsonDateString) {
            return that.toDate(jsonDateString);
        },

        toShortDateString: function (jsonDateString) {
            var date = toDate(jsonDateString);
            return dayAsDoubleDigitDayString(date) + '/' +
                   monthAsDoubleDigitMonthString(date) + '/' +
                   date.getFullYear();
        },

        toShortDateTimeString: function (jsonDateString) {
            var date = toDate(jsonDateString);
            return dayAsDoubleDigitDayString(date) + '/' +
                   monthAsDoubleDigitMonthString(date) + '/' +
                   date.getFullYear() + ' ' +
                   hourAsDoubleDigitHourString(date) + ':' +
                   minutesAsDoubleDigitMinuteString(date);
        }
    }
})();

First, i create an object (if it doesn't already exist) to serve as a namespace. Then i add a property called 'jsondateformatter' to that 'namespace object'. So far this is pretty boring. But the value that gets assigned to the jsondateformatter property is what's interesting here. As you can see, i'm wrapping a function expression between 2 braces, and all the way at the bottom of the code i add the invoke operator, which is (). That means that my function expression will be immediately evaluated (typically referred to as an immediate function), and the result of that will be assigned to the jsondateformatter property. And what exactly is the return value of my immediate function? It's an object which contains 3 properties: toDate, toShortDateString and toShortDateTimeString. Each of those properties contains a function, and each of those functions refers to one or more variables that i created in the immediate function.

And this is where it gets beautiful (at least, in my opinion and i have to admit i'm kinda weird that way): due to the power of closures, the values of the variables that i created in the immediate function remain accessible in the functions that have been assigned to the properties of the object that my immediate function returns. Every function that i've created within the scope of the immediate function can only be accessed by the functions of the object that i return, and that's it. It's completely encapsulated and reusable. And you don't need classes to do it. You might think "yeah well ok, i guess that's cool but you're still using an object so it's not all too different!" and again, you'd be right in some way. But the object i'm returning here is nothing more than some kind of holder of the 3 functions that i want to make available. If i only needed to make one function available, i wouldn't return an object... i'd just return a specific function which closes over whatever i want to keep encapsulated.

And that is one of the cool things about trying to think in the language you're working with: you learn new ways of doing essentially the same thing, though that which you thought was the 'thing' is just one of many representations of its essence. Writing that last line made my head hurt, so i think i'll just stop here.

  • http://twitter.com/JefClaes Jef Claes

    This is the reason all cool kids do JavaScript, so damn sexy.

  • Ken Tong

    1. Isn’t it the singleton in javascript?
    2. You’ll need classes if the objects created are stateful.

    • http://davybrion.com Davy Brion

      in this particular case it is a singleton, but it doesn’t really matter wrt to the point i’m trying to get across

      and no, i still won’t need classes if the objects need to be stateful. The references to the functions are actually state as well. You can use the same technique to close on other variables as well. Again, JavaScript doesn’t have classes, but it certainly supports stateful objects :)

  • http://profiles.google.com/joe.marquardt Joe Marquardt

    I still get confused by javascript. The only point of the “that” variable is so you can reference the private “toDate” function? Couldn’t you have done “this.toDate” instead?

    • http://davybrion.com Davy Brion

      depends on the value of this when the toDate function is executed… depending on how it’s invoked, ‘this’ can refer to a different object. by using the ‘that’ variable, it will always point to the right one.

      • http://profiles.google.com/joe.marquardt Joe Marquardt

        That makes sense. But, would you still need to keep track of “this” if you named the private toDate function to something else? Seems like it’s only for over-accessor-loading of the toDate function…

        • http://davybrion.com Davy Brion

          In my first version the local toDate function was actually called toDateImpl to avoid the ‘this’ issue, but i wasn’t happy with that name so i went with the ‘that-pattern’ instead. And no, if the name had been different i’d have no need (in this case) to keep track of “this”

          • Lunpa

            A suggestion:  seeing that this looks a lot like the style outlined in the book “Javascript: The Good Parts” (and if I am wrong to assume you’ve read it, I think you would enjoy it),  the book also recommends adjusting your naming convention based on context, to avoid confusion.

            For example, in the object returned by your closure, object member “toDate” obviously is a wrapper to the private variable of the same.  The other functions in this object all use the private variable “toDate”, not the wrapper for it.  Without seeing any more of the program, it is impossible to tell if the other functions in the returned object were intended to use the wrapper defined on the object, or the private variable of the same name.

            Because of this, I try to use _leading_underscore for variables intended to be hidden, MixedCase for functions intended to be used as constructors, and lower_case for most everything else.

            • http://davybrion.com Davy Brion

              i’ve read about that style in both Javascript: The Good Parts and Javascript Patterns :)
              i like both books, though The Good Parts is perhaps a bit too concise at times… Haven’t finished Javascript Patterns yet but i liked the writing style in that one more

              as for your recommendation about the naming… i agree :)  

  • plars

    I receive an error when I try call MyNameSpace.jsondateformatter.toDate function.
    var that = this; \ “this” is the Window Object.

    • http://davybrion.com Davy Brion

      you’re right… the var that = this thing has been a brainfart on my part. it’s recommended for constructor functions but in immediate functions it indeed points to the global object.

      if you remove the var that = this line, and remove the ‘that.’ in the returned toDate function, it works