When you start having objects in your code, its only natural that methods would follow. With methods there are many different ways to define them, all have various side effects so it’ll be important to know why one is preferable to the other.

Instance method in object

  1. function FooObject() {
  2.    this.mBar = function(){};
  3.    this.mBlah = fnBlah;
  4. }
  5.  
  6. function fnBlah() {}

Here we have an object FooObject, and every time we instantiate it, it gets methods of mBar and mBlah on it.

The primary thing to consider here is that every time you instantiate an instance of FooObject, a new copy of the function assigned to mBar is created. So we have a memory penalty.

We also get two extra operations which assign functions to the two method properties of our FooObject for each new instance of FooObject.

Though because mBar's function is defined inside of the FooObject, it does have access to its scope via closure. Which is something.

Instance method on prototype

  1. function FooObject() {};
  2. FooObject.prototype = {
  3.    mBar: function() {},
  4.    mBlah: fnBlah
  5. }
  6.  
  7. FooObject.prototype.mBoo = fnBoo;
  8.  
  9. function fnBlah(){};
  10. function fnBoo(){};

Assigning to the Prototype of your new class has some major advantages. The prototype object is consistent for each instance of the FooObject, so you don’t have extra operations for each method you assign on each object instance.

You can specify a new blah method that will inject itself as first into the chain to be called, yet still maintain a copy of the old method.

  1. // New FooObject with a blah method which alerts a string.
  2. function FooObject(){};
  3. FooObject.prototype = {blah: function(s){alert(s)}};
  4.  
  5. // New instance of Foo Object
  6. var x = new FooObject();
  7.  
  8. // Redefine the Blah method, now it should prompt instead of alert.
  9. x.blah = function(s) { prompt(s,s); };
  10.  
  11. // Call the original blah method which alerts.
  12. FooObject.prototype.blah.call(x, 'asdf');
  13.  
  14. // Call the blah method on our instance, it will prompt not alert.
  15. x.blah('xyz');

A note about assigning to the prototype though, if your doing inheritance, you’ve just blown up your inheritance chain. Here’s some code to show you what I mean.

  1. function Animal(){};
  2. function Cat(){};
  3.  
  4. // Cat is an animal
  5. Cat.prototype = new Animal();
  6.  
  7. // Uhhh yea, now its no longer an animal
  8. Cat.prototype = { 'meow': function(){} }

Its because of this most (all?) of the JS Libraries out there would end up Iterating over the Animal class and assigning all its properties to the Cat prototype. Its a bit of a brute force technique, but its what must be done.

The only thing I dislike about this method is how clean looking it is. I’m not a fan of having your methods declared after your object all the way down the page, its just not very easy on the eyes. But with using a method like Mootools’s Class.Implement you can still use this basic technique with it still looking good.

Static Methods

With static methods, we want to call the method from the definition of the object, not an instance of it. Since functions are essentially objects themselves, we can add whatever we like to them.

  1. function Animal() {};
  2. function Cat(){}
  3. Animal.GetName = function(petObj){};
  4.  
  5. // This works
  6. Animal.GetName(new Cat());
  7.  
  8. // FAIL!
  9. new Animal().GetName(new Cat());

Since Static methods are declared on the declaration it doesn’t get inherited. Thus, there is no Cat.GetName().

No comments

Leave a Reply