OOP in JavaScript PART3

Pattern 3: Classical Pattern

(Continued from PART2)
As you know, JavaScript doesn’t support classical inheritance. However, a lot of developers are used to classical inheritance from working with languages like Java, PHP, or Ruby. The good news is that JavaScript is so powerful that we can actually simulate a classical model.

The thing about this is that, since it’s not built in, we have to create (or find) a library that adds the classical features like super methods, and easy inheritance. It’s reasonably complicated to create this kind of thing, so we’re going to work with two already-created libraries: Base.js and Mootools. I’ll mentions right here, though, that they aren’t the only ones that do this; if you’re interested in this topic, you might want to check out the function John Resig made to do this. Also, the Dojo Toolkit offers some functionality very similar to Mootool’s.

Let’s start with Base.js. It was written by JavaScript guru Dean Edwards. It’s offers a lot of functionality, and we’re only going to be able to discuss part of it here (after, if you’re interested in finding out more, check out out his site for a few posts about base.

You can download the library here (direct link). Base provides a “base” class that you start by inheriting from. Then, any classes that inherit from that class will do so in the same way. It’s a really nice model.

Check out our person class, and then we’ll discuss it:

var Person = Base.extend({
    constructor: function (first, last, age) {
        this.first_name = first;
        this.last_name = last;
        this._age = age;
        this.interests = [];
    },
    age : function (age) {
        if (age) {
            this._age = age;
            return this;
        }
        return this._age - 5;
    },
    add_interests : function () {
        for (var i = 0; i < arguments.length; i++) {
            this.interests.push(arguments[i]);
        }
        return this;
    }
});

var bill = new Person("Bill", "Parker", 59);
console.log(bill);

We’re storing our class in the Person variable; remember to follow the convention and capitalize the first letter of your class. As you can see, we’re calling the Base.extend method. Every class created with Base.js will have an extend method, and this is how you inherit from a class. That means, to inherit from our Person class, we’ll call Person.extend. This uniformity is one of my favourite things about the library. We passextend a parameters object. The first parameter we’re passing has the key constructor. This is the constructor function for our “class.” You can write this constructor exactly as you would write a constructor for the prototypal pattern we studied earlier (i.e. write it as if new will be used). However, Base (and Mootools, too) isn’t doing a regular call via new behind the scenes, so checking for this === window won’t work here.

I should note that, because we’re writing the constructor is the same way we do in the prototype, we don’t have private variables with this pattern.

After the constructor, we can add other methods that will be “instance” methods for our “class.” This is just like putting methods on the prototype object of our function, as we did earlier.

How that we’ve got it all in place, you can give it a try in the console (or just in the bottom of your code). You should find that it works just like our previous objects.

Now, let’s create our SuperHuman class.

var SuperHuman = Person.extend({
    constructor: function (first, last, power) {
        this.base(first, last);
        this.power = power;
        this.add_interests(power);
    },
    wield_power : function () {
        return this.power.toUpperCase()  + " to the rescue!";
    }
});

var bob = new SuperHuman("Bob", "Smith", 'typing');

console.log(bob);

As we discussed, we inherit from Person by called Person.extend. Again, we pass in the parameters object. We’ve got something new going on within the constructor this time: this.base(first, last). What’s this? Part of OOP, as you’ll recall, is Polymorphism, which states that classes that inherit from other classes should have functions that do similar things named the same (this is put simply). So, if you’re inheriting class B from another class, say class A, that has a method named x, When you write a method in B called x, you can no longer get to A.x. To do this, many classical languages offer the keyword super. This isn’t built into JavaScript (although the word super is reserved), so Base.js and other libraries gives it to us. In Base.js, we can get to the super method of the method we’re currently writing by callingthis.base(), passing in any parameters we’d like. Looking at it another way, here, we’re calling the constructor function of Person and then going on to do something specific for SuperHuman. It’s important to note that we’re calling the Person constructor on the new SuperHumanobject we’re in the process of creating. Then, of course, we go on to add any methods that are for SuperHuman objects.

If you give this a try, you’ll see that it all works fine! We’ve got a properly-inheriting SuperHuman.

Let’s move on to our practical example: the tooltip.

var Tooltip = Base.extend({
    constructor: function (btn, panel) {
        this.button = jQuery(btn);
        this.panel = jQuery(panel);
    },
    show : function () {
        this.panel.show();
    },
    hide : function () {
       this.panel.hide();
    },
    bind_events : function () {
        var that = this;
        this.button.bind("mouseover.TB", function () { that.show.call(that); });
        this.button.bind("mouseout.TB", function () { that.hide.call(that); });
    },
    unbind_events : function () {
        this.button.unbind(".TB");
    }
});

var t = new Tooltip("#button", "#panel");

t.bind_events();

You should be pretty familiar with this syntax now. We’re creating a class Tooltip by inheriting from Base. We’ve got our constructor, and our other methods, just like we had before. You’ll see that this works fine if you plug it info our HTML above.

But the inheriting it pretty cool here. Because, with Base.js, if we don’t need to customize the constructor when inheriting, we can just leave it out. Our sub-class will just use its parent’s constructor. See here:

var Fading_Tooltip = Tooltip.extend({
    show : function () {
        this.panel.fadeIn();
    },
    hide: function () {
      this.panel.fadeOut();
    }
});

t.unbind_events();

var ft = new Fading_Tooltip("#button", "#panel");
ft.bind_events();

As you see, we only have to replace the two methods that are different; ignoring the constructor just inherits the previous one, which makes our job much easier. Give this a try!

One of the problems with what we’re doing here that we’re bringing two libraries into our project: jQuery and Base.js. There are several libraries that offer classical OOP functionality with the DOM leveling of jQuery … Mootools, for example. Let’s give that a try.

Before we being, I should make a few comments about Mootools. I’ve not anywhere near a Mootools expert, but I know a thing or two. Mootools has two parts: the “core” and the “more.” A lot of the extra functionality goes in the “more.” Unfortunately, we need to include both files for our project. The only reason we need mootools-more.js is because it includes the fade in / fade out functionality. Everything else we use will be available in the core. You can get these files here:

Let’s begin:

var Person = new Class({
    initialize: function (first, last, age) {
        this.first_name = first;
        this.last_name = last;
        this._age = age;
        this.interests = [];
    },
    age : function (age) {
        if (age) {
            this._age = age;
            return this;
        }
        return this._age - 5;
    },
    add_interests : function () {
        for (var i = 0; i < arguments.length; i++) {
            this.interests.push(arguments[i]);
        }
        return this;
    }
});

We won’t dwell on this too long, because—as you’ll see—it’s very similar to the Base.js syntax. The primary differences are these:

  • To create the “class,” we call new Class, passing in the object of parameters.
  • The constructor function is called initialize, instead of constructror.

Currently, those are the only differences; this should work just like the others have.

When we start inheriting, we depart a little more from the Base.js syntax:

var SuperHuman = new Class({
    Extends : Person,
    initialize: function (first, last, power) {
        this.parent(first, last);
        this.power = power;
        this.add_interests(power);
    },
    wield_power : function () {
        return this.power.toUpperCase()  + " to the rescue!";
    }
});

To inherit, we add a property named Extends; the value of this property is the class we want to inherit from. Then, we access super methods via this.super, as you can see in the initialize function. Not too hard, eh? I know you’re tired of me telling you to try it out, so I won’t this time.

Let’s move on to our DOM example. You could probably do this yourself, by now, since it’s so similar to the methods we’ve already seen. However, there’s going to be some differences in the way we handle events. (If you’ve seen the screencast that goes along with this part, you’ll know I had some problems with Mootools events; we’ll solve that here.) Here’s the code for our TooltipButton:

var TooltipButton = new Class({
    initialize: function (btn, panel) {
        this.button = document.id(btn);
        this.panel = document.id(panel);
        this._event_fns = {};
    },
    show : function () {
        this.panel.show();
    },
    hide : function () {
       this.panel.hide();
    },
    bind_events : function () {
        this._event_fns.show = this.show.bind(this);
        this._event_fns.hide = this.hide.bind(this);
        this.button.addEvent("mouseover", this._event_fns.show);
        this.button.addEvent("mouseout", this._event_fns.hide);
    },
    unbind_events : function () {
       this.button.removeEvent("mouseover", this._event_fns.show);
       this.button.removeEvent("mouseout", this._event_fns.hide); 
    }
});

This problem we had in the screencast was that, since Mootools doesn’t have event namespacing the way jQuery does, we have to keep track of the functions we bind to events. Here’s what I’ve done differently:

  • I’ve added a “private” property in the constructor; this is just an object that will keep track of our bound functions.
  • I’ve added those functions to the object before binding them in the bind_events function. This way, we can point to those functions in memory when we come to unbind them.

You might notice that instead of creating the mouseover and mouseout functions like function () { that.show.call(that); }, we are doingthis.show.bind(this); why the difference? Well, remember when we talked about call and apply, I mentioned that there’s a bind version coming, that returns a copy of the function bound to the desired context? Mootools adds this functionality to existing browsers, so we can use that instead of wrapping it ourselves.

You know what’s coming: try it with this:

var tb = new TooltipButton("button", "panel");

tb.bind_events();

As before, inheritance is simple:

var FadingTooltipButton = new Class({
    Extends: TooltipButton,
    show : function () {
        this.panel.fade("in");
    },
    hide : function () {
        this.panel.fade("out");
    }
});

var ftb = new FadingTooltipButton("button", "panel");
tb.unbind_events();

ftb.bind_events();

Unlike the screencast, this unbind_events function will work. Just to keep this complete I’ll mention one more thing. As you know, the tooltip panel is not showing when the page loads; it’s styled display:none. This works fine for jQuery’s fading functions; however, Mootools uses the visibility property. So, to see this fading correctly, change display:none to visibility:hidden. Of course, this has nothing to do with JavaScript, but just in case it troubles you, that’s your fix.


Pattern 4: Prototypal Pattern (Revised)

We’ve already discussed the prototypal object creation and inheritance that JavaScript has supported for a while. However, the ECMAScript standard, 5th edition, gives us another way to do prototypal inheritance, a way that I think is much cleaner and prettier. More than that, it gives us a great “toolkit” for creating another—more powerful—pattern, that we’ll see in the next section.

This pattern uses the new function Object.create. Not every browser supports this function yet; however, we can add it for those that don’t. Try this:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
}

We wrap this in an if-statement so that we don’t over-write the native functionality if the browser supports this method. If the browser doesn’t it is safe to add it. We do so by creating a blank constructor function and setting it’s prototype equal to the object we passed in. Then, we return a new instance of that function. This gives us a clean object with the desired prototype.

We can start this pattern by creating our prototype object; it’s just an object literal:

var person = {
    first_name : "john",
    last_name  : "doe",
    _age       : 20,
    age        : function (age) {
        if (age) {
            this._age = age;
            return age - 5;
        }
        return this._age - 5;
    },
    add_interests : function () {
        this.interests = this.interests || [];
        for (var i = 0; i < arguments.length; i++ ){
            this.interests.push(arguments[i]);
        }
    }
};

This is our prototype; it has all the methods we’ve “traditional” put on the prototype object, as well as some default values from properties; the properties are, of course, optional; we’ll over-write them once we create an “instance” of our object.

You might notice a difference between this and what we’ve done previously: where’s the interests array? Remember that we’ve never put the interests array on the prototype; we always added it to the object itself inside the constructor; however, this method doesn’t have a constructor (it will in the next pattern). If we added the array to the prototype, every object that uses this as its prototype would share the same interests array, because arrays are reference values. There are a few ways you could solve this problem; the best way is probably to use the next pattern. For now, we’ll check for the array at the beginning of the add_interests function; if it’s not there, we’ll add it. As long as this isn’t called on the prototype (and there’s no reason it should be), everything will be fine.

To use this prototype, we call Object.create, passing in person as our prototype. Then, we must set all the custom properties that make our object unique:

var jill = Object.create(person);

jill.first_name = "Jill";
jill.last_name = "Taylor";
jill.age(28);
jill.add_interests("Hiking");

As you can see, there’s a real element of “raw power” here; we have the ability to quickly make copies of object and define prototypes for other objects, but we still have to customize them by hand.

When we’re ready to create our SuperHuman, we do so in the same way we create an “instance” of person.

var superHuman = Object.create(person);
superHuman.wield_power = function () {
    return this.power.toUpperCase() + " to the resuce!!!1!!!1";
}

You’ll notice that there’s no difference between the way we’re creating “instances” of person and our superHuman prototype. In both cases, we just create a new object with person as the prototype, and then add our custom functionality. To me, that’s part of the beauty of this method.

var jane = Object.create(superHuman);

jane.first_name = "Jane";
jane.power = "Hugs";
jane.add_interests("Hugs");

Let’s move on to our DOM example. As before, we start by creating the prototype for our tooltip:

var tooltip = {
    show : function () {
        this.panel.show();
    },
    hide : function () {
        this.panel.hide();
    },
    bind_events : function () {
        var that = this;
        this.button.bind("mouseover.Tooltip_Button", function () { that.show.call(that); });
        this.button.bind("mouseout.Tooltip_Button", function () { that.hide.call(that); });
                  },
    unbind_events : function () {
        this.button.unbind(".Tooltip_Button");
    }
};

This time, I’m not including defaults for the properties … but I’m not ever checking to see if the user has added those properties either. Of course, this is a personal code decision, but I think it’s fine to assume the user will do the right thing, given the “raw power” of the pattern we’re using. That’s why this prototype only has the methods that will be common to all tooltips.

When we create a tooltip, we can’t forget to add those properties:

var t = Object.create(tooltip_button);

t.button = jQuery("#button");
t.panel  = jQuery("#panel");

t.bind_events();

As we step back into inheritance here, remember that there’s no difference between the way you create an “instance” of a prototype and a “sub-prototype.” Yes, we can do the thing you’re expecting:

var fading_tooltip= Object.create(tooltip_button);

fading_tooltip.show = function () {
    this.panel.fadeIn();
};
fading_tooltip.hide = function () {
    this.panel.fadeOut();
};

But there’s another interesting idea here. We already have a normal tooltip; we could just modify that object with the fading functions. Essentially, we’ll “convert” that tooltip to a fading one. Then, if we need other fading tooltips, we just use that as the prototype.

t.show = function () {
    this.panel.fadeIn();
};
t.hide = function () {
    this.panel.fadeOut();
}

// to create a second fading tooltip:
Object.create(t);

You’ll probably have to unbind the old events and rebind new ones, but that’s just two function calls. This really makes us realize that there’s no difference between prototype objects and the objects that inherit from them. We can use any object as a prototype withObject.create … even an object that’s being used in another way. This is one of the things that makes the Object.create function really valuable, and a much better implementation of prototypal inheritance than the old constructor functions and their prototype properties.

Pattern 5: “Crockford” Patterns (Continued in Part 4...)

Loading Facebook Comments ...

2 thoughts on “OOP in JavaScript PART3”

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>