Saturday, September 24, 2011

How I like to define JavaScript Objects

I've been really impressed by the expressiveness and power of JavaScript lately.  It's much more capable a language than I ever used to give it credit for.  The flexibility of JavaScript also can make it often harder to understand what is going on.  There are many ways to accomplish the same thing and they can look like completely different things.

If you have need to create a lot of custom "classes" in your application, I have a few things I've grown to appreciate.  In this post I'll talk about a style of class definition that I find readable, provides good encapsulation and can provide good performance when accessing functions on these objects.  I'm sure that if I'd worked with JavaScript longer I'd be able to enumerate all the problems with my approach, but so far I don't think there is a lot of down side. But I think part of the reason I like my approach is that it reminds me of Java with less ceremony.

Here's what I usually see around the web:
function MyClass (some, params, needed) {
    this.doSomeInit();
}
var p = MyClass.prototype; //Or = new ParentClass();
p.baz = "lorem";
p.fuzz = "ipsum";
p.foo = function() {
    return this.baz + this.fuzz;
};
p.bar = function() {
    return this.fuzz + this.baz;
};
I've also seen:
function MyClass (some, params, needed) {
    this.doSomeInit();
}

MyClass.prototype.baz = "lorem";
MyClass.prototype.fuzz = "ipsum";
MyClass.prototype.foo = function() {
    return this.baz + this.fuzz;
};

MyClass.prototype.bar = function() {
    return this.fuzz + this.baz;
};
So what's going on?  
The above code is basically just 2 different ways of defining a JavaScript "class".  I put class in quotes because JavaScript doesn't technically have classes.  It has objects.  My main gripe with these approaches is that it requires that these objects expose a lot of their internal structure by updating their prototype so that these public functions can manipulate the internal state of the object and do work. The only real difference in the 2 styles above is that one is slightly more efficient since it avoids the chaining of object property calls that's required.  There is a cost to calling MyClass.prototype each time you define a new function.  So if you're looking for speed you put that in a variable.

I prefer to do something like the following:
function MyClass (some, params, needed) {
    var baz = "lorem";
    var fuzz = "ipsum";

   function myPrivateFoo() {
       console.log("PRIVATE");
   }

    this.foo = function () {
        return baz + fuzz;
    };    
    this.bar = function () {
        myPrivateFoo();
        return fuzz + baz;
    };
}
This approach means that I don't have to expose the variables baz and fuzz.  I also get to use the parameters, "some", "params", "needed" without the need to create internal vars or properties on the prototype.  The functions foo() and bar() are both public privileged members of our object.   It also makes it easy for these public functions to use private functions.  My main reasons for liking this approach is that I think it's cleaner in that I like that my constructor encapsulates the class definition like you'd see in Java. I also like that my object can be more shy about what they expose to the world.  But is there a downside to this?  Actually there is.  

The catch is that every time the constructor is called all of the code in my constructor gets run.  All the function definitions are added to the prototype every time.  That's not so good.  Why?  Because if your custom object needs to be constructed a lot, then yes you'll take a hit in the performance department.  However that's usually a smell of something else not being quite right in the design and can be mitigated with a pattern that separates construction from execution and ensuring you are being judicious about your object creation. However if it truly is something that needs to be created thousands of times, then by all means follow one of the other approaches.  Or take a hybrid approach and define any functions that are fully public and don't require internal knowledge of your object state and define them explicitly with the MyClass.prototype.myFunction syntax and anything that has to access the internal state can be defined in the constructor.  There's not really a right answer here.  I'm just sharing my preference and providing a small argument for it.  

If you were to look at performance from another perspective, my approach may actually be preferred.  If you are using functions that an object exposes in a loop, or another pattern that requires lots of calls, my approach means that there are fewer property lookups on the object prototype since local variables can be used more often.  In practice I find that I'm not constructing custom objects very frequently, but I am calling functions on those objects in loops quite a bit.  So for me this approach is a good fit.  

Anyone else have a different take? I'd love to hear about it in the comments.

8 comments:

  1. I'm not knowledgable about JavaScript performance, but I've found in languages that don't impose access control to the elements of an object, it's best not to fight it. That is to say, don't go out of your way to try and prevent outside entities from accessing your internal state. Instead, make them feel bad for doing so.

    Lisp and Python are good at this. In Lisp, you must use the ugly double colon syntax to access an unexported symbol in a package (package::internal-symbol); Python uses the convention of starting private names with an underscore (again, ugly).

    If you fight the language, you will eventually lose.

    Just sayin'. :)

    ReplyDelete
  2. That makes some sense. But what I suggest here is in line with what guys like Crockford suggest. I don't think this is fighting the language per se. I think this is getting at some encapsulation by working within the language. http://javascript.crockford.com/private.html

    If you'd like to take a look at an effort to basically re-make JavaScript into JavaScript with classes take a look at the Closure Library and Closure Compiler that Google's been building. They've even tacked on some type safety. ;-)

    ReplyDelete
  3. I think an example of something that would be fighting the language would be to go out of your way to make an object immutable in JavaScript. That would be interesting to see someone try and do. ;-)

    ReplyDelete
  4. Closures are good for encapsulation. That's definitely the way to go.

    I'm in the camp of "type safety and encapsulation are overrated", though, so take what I say with a grain of salt. :)

    ReplyDelete
  5. I'm definitely more shy than you are...so to speak.

    ReplyDelete
  6. Type safety and encapsulation are important when you need them to keep people in check... some people can't be trusted :).

    ReplyDelete
  7. A pitfall of my suggested approach that I'd not really considered previously is that when you instantiate an object the instance will actually take up more memory. As such, truly public methods that don't require accessing private internal members really should be defined using prototypes. My approach I think is cleaner in that everything is kept nicely in the function block, but given the state of memory management in JavaScript and targeting embedded platforms, saving memory is always a good thing. So keep that in mind as well.

    ReplyDelete
  8. Hi, I have developed a lightweight inversion of control (IoC) container for TypeScript, JavaScript & nodejs apps. You can learn more about it at http://blog.wolksoftware.com/introducing-inversifyjs I hope you like it!

    ReplyDelete