Chapter 5. Classes: The Extras

The things we've learned about the MooTools class system in the previous chapter fall under the category of "essential knowledge." While they form the foundation of what we need to know about classes, there are several other topics related to the classes that we need to discuss. Instead of cramming them all together, we set aside a separate chapter for these additional topics.

In this chapter, we'll learn about the "extras" that are part of the MooTools class system and how we can use them to create better and more flexible classes.

Mutators

In a class declaration, adding the Extends and Implements keys changes the class itself: the first one makes a class inherit from a superclass, while the second adds properties and prototypes from mixins into the class. In the last chapter, we called these two as "special keywords," but they're really functions. In MooTools, we call these special functions as mutators.

As the name implies, a mutator is a function that changes or "mutates" the class. In some programming languages, such functions are called class macros because they transform classes according to various user-defined rules.

In MooTools, all mutators are stored in the Class.Mutators object. The Extends keyword in a class declaration corresponds to the mutator function Class.Mutators.Extends, and the same goes for Implements, which corresponds to Class.Mutators.Implements.

Note

Don't confuse the use of the word mutator in MooTools with its use in some classical languages. A mutator in classical terms is what we usually refer to as a "setter" method, which is a method that changes the value of an instance's property, in contrast to an accessor or "getter" method, which returns the property. As noted above, MooTools' mutators are actually class macros or functions that transform a class.

Invoking the mutator that corresponds to a key is the job of the implement method. For every key passed to it, the implement method checks Class.Mutators to see if there's a mutator function that corresponds to the key. If a mutator is found, implement invokes the function and passes the value of the key as an argument. To illustrate, let's look at this class declaration:

var Duck = new Class({

    Extends: Bird,
Implements: [Events, Options],

    options: {
        name: '',
        color: ''
    },

    initialize: function(name){
        this.setOptions(name);
 }

});

In this example, we're declaring the Duck class by passing an object literal containing our members to the Class constructor. The Class constructor, after doing some preliminary processing, then calls the implement method to add the members to our new class. The implement method goes through each item in the object literal, starting with Extends, and checks whether there's a mutator named Extends in Class.Mutators. Since there is one, it calls Class.Mutators.Extends using the call method, binding the Duck class as the this keyword value and passing in the Bird class as an argument. It then does the same thing for Implements, which is also a mutator, but this time passing the array [Events, Options] as an argument. The initialize method, on the other hand, isn't a mutator and there's no Class.Mutators.implements, so it's added to the class just like a regular method.

Most class transformations are done via mutators, and they can be used to implement features from classical language easily. Take the two built-in mutators, Extends and Implements, for instance. They may be doing complex transformations to facilitate inheritance, but they're actually pretty simple functions. Extends, for example, which is the mutator that handles subclassing, does its job using only four lines:

Class.Mutators.Extends = function(parent){
    this.parent = parent;
    this.prototype = getInstance(parent);
};

The Extends mutator has its this keyword bound to the current class and accepts one parameter, parent, which refers to the superclass. When the mutator is called, it first adds a parent property to the current class and assigns the superclass as its value. It then creates a new, uninitialized instance of the superclass using the private getInstance function and sets the prototype of the current class to this instance.

Applying this to our previous Duck class example, the Extends mutator is called and passed the Bird superclass as an argument. The mutator then sets the value of Duck.parent to point to the Bird class. Finally, a new, uninstantiated instance of Bird is created and set as the value of Duck.prototype.

In contrast, the Implements mutator looks a bit more complex:

Class.Mutators.Implements = function(items){
    Array.from(items).each(function(item){
        var instance = new item;
        for (var key in instance) implement.call(this, key, instance[key], true);
    }, this);
};

As with the Extends mutator (or any other mutator for that matter), the this keyword of the Implements mutator is also bound to the current class. It receives a single argument, items, which could be a single mixin class or an array of mixin classes. The first thing it does is to make sure that the items argument is an array by passing it to Array.from. It does this so that it can use the same code for either a single mixin or an array of mixins. It then loops though each of the mixins using the each array method, which takes as an argument a callback function that will be invoked for each item in the array, and a second optional argument that will be used as the this value of the callback function. In this example, we set the this value of the callback function to the current class by passing the this keyword as the second argument to the each method.

The callback function itself receives a single argument item, which is a reference to the mixin, and it then creates an instance of the mixin. It then loops through the instance and adds each member to the current class using the implement function, which is a private version of the implement class method.

Applying this to our previous example, the Implements mutator is called and passed an array of mixins, [Events, Options], which becomes the value of the items variable. Because items is already an array, Array.from returns the same array and the mutator function loops through each item in the array. For each item, the callback function is called and is first passed the Events class. The callback function then creates an instance of the Events class and goes through each of its members and adds them to the Duck class using the implement function. The each method then moves to the next item in the array, Options, and the callback function is called once more and the process is repeated.

Amazingly, it only takes those two simple functions to form the core of MooTools' implementation of classical inheritance. When used with the class system, mutators enable us to create complex transformations using simple functions. But to fully understand the power of mutators, we'll need to implement our own mutator function.

Implementing Your Own Mutators

The mutator we'll implement is an easy one: a mutator for creating getter and setter methods. Take a look at the following class definition:

var Person = new Class({

    $name: '',
    $age: 0,
    $occupation: '',

    setName: function(name){
        this.$name = name;
        return this;
    },

    getName: function(){
        return this.$name;
    },

    setAge: function(age){
        this.$age = age;
        return this;
    },

    getAge: function(){
        return this.$age;
    },

    setOccupation: function(occupation){
        this.$occupation = occupation;
        return this;
    },
getOccupation: function(){
        return this.$occupation;
    }

});

var mark = new Person();
mark.setName('Mark'),
mark.setAge(23);
mark.setOccupation('JavaScript Developer'),

console.log(mark.getName() + ', ' + mark.getAge() + ': ' + mark.getOccupation());
// 'Mark, 23: JavaScript Developer'

A few things about this class: first, you'll see that we prefixed all our properties with the dollar ($) character. JavaScript allows identifiers to begin with the dollar character, and it's MooTools style to mark "internal" properties—properties that should not be accessed directly outside the object—by prefixing them with the dollar character (more on this later). So in this example, $name, $age, and $occupation are considered internal properties and they shouldn't be accessed directly outside the object, which is why we have three pairs of getter and setter methods to modify them.

Another thing you'll notice is that we have a lot of repetition: the bodies of our three pairs of getters and setter are similar—the only difference among them is the name of the property they're accessing.

If we need to add another property, we'll have to write another set of those methods. This leads to unnecessary work. Wouldn't it be easier if we could automate this? We should, in fact, and we're going to implement a mutator to automatically create the getter and setter methods.

Creating a mutator is as simple as augmenting Class.Mutators. Mutator names should always be capitalized in order to differentiate them from normal class members: it's easier to see which keys correspond to mutators if the keys are visually different. Here, we'll choose an appropriate name for our mutator, GetterSetter, since it creates getter and setter methods.

Let's see how we can create this mutator. First, we'll need the mutator function to know the names of the properties for which methods will be created. These names will be the arguments to our function. As with the Implements mutator, we want to be able to receive a single property name or an array of property names. Then, we'll need to implement the getter method for that property using the implement method. We'll also have to do the same for the setter method.

With these specifications, we'll end up with something like this:

Class.Mutators.GetterSetter = function(properties){
    var klass = this;
    Array.from(properties).each(function(property){
        var captProp = property.capitalize(),   // changes 'prop' to 'Prop'
            $prop    = '$' + property;          // changes 'prop' to '$prop'

        // setter method
        klass.implement('set' + captProp, function(value){
            this[$prop] = value;
            return this;
        });

        // getter method
        klass.implement('get' + captProp, function(value){
            return this[$prop];
        });
    });
};

Before I explain how this works, though, let's see how it's used:

var Person = new Class({

    GetterSetter: ['name', 'age', 'occupation']

});

var mark = new Person();
mark.setName('Mark'),
mark.setAge(23);
mark.setOccupation('JavaScript Developer'),

console.log(mark.getName() + ', ' + mark.getAge() + ': ' + mark.getOccupation());
// 'Mark, 23: JavaScript Developer'

Nifty, isn't it? We turned that complex class declaration into a simple one that uses only a single mutator. This and the previous examples generate the same class, but this one is simpler to write since it's automated.

Our GetterSetter mutator might look more complicated than the built-in mutators we've seen so far, but they work using the same principles. Like the Implements mutator, GetterSetter accepts a single string or an array of strings as an argument. These strings will be used for the name of the property and the name of the methods. We also use the same Array.from function to turn the arguments into an array, and we use the each array method to loop through each item in the array.

In the first line of our GetterSetter mutator, you'll see a weird declaration: var klass = this;. This is a MooTools idiom that solves the issue of object referencing within functions. Remember that inside our mutator, the this keyword points to the class itself. However, within the callback function for the each array method, the this keyword points to the array and not the class. In the Implements mutator, we bound the callback function to the class by passing a second argument to each. However, in our GetterSetter mutator, we forgo the binding by declaring a local variable klass instead. Since the klass variable is available to our callback function due to lexical scoping, we will still be able to reference the class itself without having to rebind. Another reason we did this is to avoid confusion: since we'll need to use the this keyword inside the getter and setter methods themselves, it'll be easier to track which object we're talking about if the class itself is given a different identifier.

Note

We used the identifier klass in this example because class is a reserved word—it can't be used as an identifier. In MooTools Core, the idiom is usually written as var self = this;.

The actual implementation of the getter and setter methods to the class happens inside the callback function. The first thing the callback function does is to transform the string property name into two forms: one capitalized using the string-capitalize method, and the other prefixed with the $ character. The capitalized version of the name is stored in the captProp variable and then used for the method names, while the $ prefixed version is stored in $prop and used inside the methods themselves to access the property. We then called the implement method of the class to add our methods.

When applied to our Person class, the mutator assigns the Person class as the value of the klass variable, and it receives the argument ['name', 'age', 'occupation']. This array is then passed to Array.from, which returns it without modification. Using the each method to loop through the array, the callback function is called and it first receives the property 'name' and transforms it into two forms: 'Name' and '$name'. The implement method of the Person class is then called, passing in getName and the function that will become the actual getName method. The same is then done for the remaining properties.

When getName or any other method added by the mutator is called, it accesses the property using this[$prop]. For getName, $prop has the value '$name', so this[$prop] is the same as accessing this.$name. Because of closures, the getName method retains the value of these variables outside of the GetterSetter mutator. The result is a very easy way to implement getter and setter methods using only a mutator.

Mutator Gotchas

While mutators themselves are easy to understand, their use can sometimes lead to weird behavior. One important thing to remember is that mutators are applied according to the order they're declared. As each mutator is processed, it can affect the already implemented members of the class and this leads to unexpected results.

Let's take a look at some tricky behavior you might encounter:

var Super = new Class({

    log: function(){
        console.log('Super'),
    }

});

var Mixin = new Class({

    log: function(){
        console.log('Mixin'),
    }

});

var Sub = new Class({

    Extends: Super,

    Implements: Mixin

});

var obj = new Sub();
obj.log(); // ?

What do you think is the value that will appear when we call obj.log()? If you answered 'Mixin', you are right. First, Extends: Super makes the Sub class inherit from Super, which means that Sub will inherit the log method from Super via its prototype chain. But because the Implements mutator adds the methods from the Mixin class directly into the current class, the Sub class gets its own log method that overrides the original log method from Super. The result is that we get Mixin instead of Super when we call obj.log(). So, how about the next example?

var Super = new Class({
log: function(){
        console.log('Super'),
    }

});

var Mixin = new Class({

    log: function(){
        console.log('Mixin'),
    }

});

var Sub = new Class({

    Implements: Mixin,

    Extends: Super

});

var obj = new Sub();
obj.log(

Here, we flipped the declarations: Implements now comes before Extends, so what's the value of obj.log()? This is a tricky snippet and even I got confused when I first encountered it. Without knowing the internals of the MooTools class system, we'd probably say that the value would still be Mixin, like our previous snippet, since the prototype chain is still in effect. Implements will add the log member of the Mixin class to the Sub class, and then Extends will make Sub inherit from Super. Because Implements adds the log method directly, it will override the log method of Super, which is what we'd expect from how the prototype chain works. So it's Mixin, right? Actually, the answer is that obj.log() will output Super.

You're probably asking, "Huh? That's unexpected!" First, recall what I said about how the order of declaration affects the behavior of mutators: because each mutator is called according to the order in which it was declared, mutators can affect any members already implemented in the class. At the start of the declaration, we first used Implements to add the log method from the Mixin class to Sub, so now Sub has its own log method. And then we used Extends to make Sub inherit from Super. But now, remember how the Extends mutator actually works: first, it assigns the superclass to the parent property of the subclass and then it creates an instance of the superclass and assigns it as the prototype of the subclass.

And that explains the weird behavior. Because Extends assigns a new value to the prototype property of the subclass, the old prototype object is destroyed—as well as any properties or methods it already contains. This means that when we did Extends: Super in our example, a new instance of Super was created and it replaced the old Sub.prototype object that contained our log method from Mixin. Thus, obj.log() outputs Super rather than Mixin. Essentially, declaring Extends in your class effectively destroys all properties and methods you've already added prior to the Extends declaration.

An Implements declaration also affects previously implemented members:

var Mixin = new Class({

    log: function(){
        console.log('Mixin'),
    }
});

var Sub = new Class({

    log: function(){
        console.log('Sub'),
    },

    Implements: Mixin

});

var obj = new Sub();
obj.log(); // 'Mixin'

In this snippet, we declared the log method of the Sub class first before we declared Implements. When the implement class method parses this, it first adds the log method from our Sub declaration to Sub.prototype. But when we get to the Implements declaration, the methods of Mixin are then added to the class, and because the Mixin class has its own log method, it overwrites the original log method that has already been implemented. Therefore, we get Mixin in our obj.log() invocation rather than Sub.

But even though these quirky behaviors are present in the MooTools class system, they're easy to dodge if you know how the system works and if you follow the recommend style. Good MooTools developers always write their mutator declarations at the start of their class declarations, with Extends coming first since it's the most "destructive" mutator. Implements then comes next, since it also overrides any previous methods, and then any other mutator you might use. If you use proper MooTools style, gotchas such as these won't ever be a problem.

The Built-in Mixins

In the previous chapter, we learned that mixins provide a way to implement multiple inheritance in classes. We learned that mixins are reusable classes whose properties and methods are added to a class directly, which is different from prototypal inheritance where members are inherited using the prototype chain.

MooTools includes three built-in mixins: Chain, Events, and Options. These mixins are used throughout MooTools, and their behavior affects the way most MooTools classes work.

The Chain Mixin

The Chain mixin is a simple class that enables us to add chainable call queues to classes. This has very special applications for asynchronous programming, as we'll see in later chapters, but right now we'll present a simple example to show how this mixin can be used.

Suppose we have the following class:

var Phone = new Class({

    busy: false,

    call: function(name){
        console.log('Calling ' + name);
        this.busy = true;
(function(){
            console.log('End call to ' + name);
            this.busy = false;
        }).delay(5000, this);

        return this;
    }

});

var phone = new Phone();
phone.call('Tim'),

Here we have a simple class called Phone with a single method called call. When we invoke this method, it sets the busy property of the instance to true, indicating that the instance is currently occupied with an operation. It then creates a timer function that is automatically invoked after 5000 milliseconds (or five seconds) that will then change the busy status of the instance to false.

Now suppose we want the class to accept calls only when it's not busy. We could do this by adding an if statement at the start of our method that will check the busy property of the instance.

var Phone = new Class({

    busy: false,

    call: function(name){
        if (this.busy) return this;

        console.log('Calling ' + name);
        this.busy = true;

        (function(){
            console.log('End call to ' + name);
            this.busy = false;
        }).delay(5000, this);

        return this;
    }

});

var phone = new Phone();
phone.call('Tim'),
phone.call('Chris'),

In this snippet we invoke the call method twice. For the first invocation, the busy property is false, so the function continues execution. For the second invocation, though, the busy property will be true because it was just set by the first invocation, and the delayed function that resets the busy property won't have been fired yet because it takes five seconds before a call is ended. Thus, the second invocation will be ignored.

But what if we want to keep that second invocation? Instead of completely ignoring it, can we somehow store this second invocation and simply call it after the first call is done? That's where the Chain mixin comes in.

The Chain mixin adds a special method called chain to classes that implement it. This method accepts a single argument, fn, which should be a function, and then stores this function in an internal call queue that can be accessed later.

var Phone = new Class({

    Implements: Chain,

    busy: false,

    call: function(name){
        if (this.busy){
            var bound = this.call.bind(this, name);
            this.chain(bound);
            return this;
        }

        console.log('Calling ' + name);
        this.busy = true;

        (function(){
            console.log('End call to ' + name);
            this.busy = false;
        }).delay(5000, this);

        return this;
    }

});

var phone = new Phone();
phone.call('Tim'),
phone.call('Chris'),

Here we added the Chain mixin to our Phone class and we modified our call method. Instead of simply returning the instance if the busy property is true, we also created a bound version of the call method that keeps the current argument. What happens now is that when we invoke call while the instance is busy, the instance uses the chain method to store this invocation into the call queue, and we can then use it later. The second invocation to call is no longer ignored, but is kept for later use.

This brings us to the second part of the equation: the callChain method. This method is used to invoke the first function on the call queue. It accepts a variable number of arguments, all of which will be passed to the function from the queue that's being invoked.

var Phone = new Class({

    Implements: Chain,

    busy: false,

    call: function(name){
        if (this.busy){
            var bound = this.call.bind(this, name);
            this.chain(bound);
            return this;
        }
console.log('Calling ' + name);
        this.busy = true;

        (function(){
            console.log('End call to ' + name);
            this.busy = false;
            this.callChain();
        }).delay(5000, this);

        return this;
    }

});

var phone = new Phone();
phone.call('Tim'),
phone.call('Chris'),

In this snippet we modified our delayed function to invoke callChain after it has set the busy property back to false. What happens now is that when callChain is invoked, it checks whether there were previous calls saved in the queue, and calls the first of these saved calls. The result is that all our function calls are now queued.

As I mentioned, the Chain mixin is used prominently in asynchronous MooTools classes, like Request and Fx. We'll take a look at how these classes use the Chain mixin later, but now we'll move on to another class that's used heavily in MooTools.

The Events Mixin

The Events mixin is used to add callback functionality to classes. An event, to define it very generally, is something interesting that happens, and classes can use the Events mixin to tell other parts of a program that something interesting has happened.

Reusing our previous Phone example, let's say we want to make our class more flexible by separating the logical and presentational parts of it. The logical part of the class deals with how the class actually works in terms of changes in state and data, while the presentational parts deal with how these changes are shown to the user. The toggling of the busy status of the class is logical, while the logging of the name is presentational. With this in mind, let's split up our class into several parts:

var Caller = new Class({

    busy: false,

    call: function(name){
        this.busy = true;

        (function(){
            this.busy = false;
        }).delay(5000, this);

        return this;
    }

});
var Display = new Class({

    callStart: function(name){
        console.log('Calling ' + name);
    },

    callEnd: function(){
        console.log('End call to ' + name);
    }

});

var Phone = new Class({

    initialize: function(){
        this.caller = new Caller();
        this.display = new Display();
    },

    call: function(name){
        this.caller.call(name);
    }

});

We divided the main components of the Phone class into two separate classes: the Caller class, which deals with toggling the busy status of the instance, and the Display class, which is used to log the call data. We then created a new Phone class that wraps these two classes together.

However, we now run into a problem: because our Caller and Display classes are now separate, there's no way to glue them together. In particular, the Display class no longer has a way to determine when to display the information using console.log. What we need then is a way for the Caller and Display classes to communicate—even though they're now separate classes.

This brings us to the Events mixin. Using this built-in mixin, we can turn our Caller class into an event dispatcher, which is simply an object that can produce events. We then use the fireEvent method from the Events mixin, which is the method used to broadcast that an event has happened. This method takes one main argument, type, which is a string denoting the type of event that will be dispatched, and an optional argument, args, which should be a value or array of values to be passed to the event handlers.

var Caller = new Class({

    Implements: Events,

    busy: false,

    call: function(name){
        this.busy = true;
        this.fireEvent('callStart', name);

        (function(){
            this.busy = false;
            this.fireEvent('callEnd', name);
        }).delay(5000, this);
return this;
    }

});

In this snippet we modified our Caller class to use the Events mixin via Implements. We then added two calls to fireEvent, the first right after setting the busy property to true to dispatch the callStart event, and the second inside our delayed function to dispatch the callEnd event. In both invocations we passed the name variable as the second argument, which in turn will be passed to the event handlers.

Dispatching the events, though, is only half of the story. Even if our Caller class now has the ability to broadcast events, those events are useless unless they're heard. And that's where event handlers come in. An event handler is a function that listens to a class, waiting for a particular event to happen. When an event is dispatched, the event handlers associated with the event are invoked. This is how the classes communicate.

Event handlers are "attached" to the event dispatcher object. In our case, we need to attach our event handlers to the Caller instance. We can do this using the Events mixin's addEvent method, which takes two arguments: type, which is a string denoting the event name, and fn, which is the event handler function.

var Caller = new Class({

    Implements: Events,

    busy: false,

    call: function(name){
        this.busy = true;
        this.fireEvent('callStart', name);

        (function(){
            this.busy = false;
            this.fireEvent('callEnd', name);
        }).delay(5000, this);

        return this;
    }

});

var Display = new Class({

    callStart: function(name){
        console.log('Calling ' + name);
    },

    callEnd: function(){
        console.log('End call to ' + name);
    }

});

var Phone = new Class({
initialize: function(){
        this.caller = new Caller();
        this.display = new Display();

        this.caller.addEvent('callStart', this.display.callStart);
        this.caller.addEvent('callEnd', this.display.callEnd);
    },

    call: function(name){
        this.caller.call(name);
    }

});

Here we updated our Phone class's initialize method with two calls to the caller object's addEvent method. In the first invocation we attached the callStart method of the display object as the callStart event handler, and the second object does the same with the callEnd method, but for the callEnd event. The result is that our two separate classes can now communicate using events.

Events are useful not only for classes but also for a host of other programming purposes. We'll learn more about events and event-based programming in Chapter 10.

The Options Mixin

A common MooTools idiom is the use of special option object arguments to class constructors as a way of customizing class instances. As its name implies, the Options mixin makes it easy to implement this idiom in your classes.

The Options mixin adds a single method called setOptions that takes a variable number of option object arguments and adds them to the options object property of the class.

var Person = new Class({

    Implements: Options,

    initialize: function(options){
        this.setOptions(options);
    },

    log: function(){
        console.log(this.options.name + ', ' + this.options.age);
    }

});

var mark = new Person({name: 'Mark', age: 23});
mark.log(); // 'Mark, 23'

Here we created a new Person class that implements the Options mixin. We then created an initialize method for the class that accepts an options argument, which we then pass to the setOptions method of the class. The setOptions method will then take this object and add its properties to the options property of the instance. We're able to access this options property in all other parts of the class, which is how we were able to get options.name and options.age in our log method.

A more powerful use of the Options mixin, though, is for setting default options for our classes. We can do this by declaring an explicit options property in our class that contains the default values for our options.

var Person = new Class({

    Implements: Options,

    options: {
        name: 'Blah',
        age: 0
    },

    initialize: function(options){
        this.setOptions(options);
    },

    log: function(){
        console.log(this.options.name + ', ' + this.options.age);
    }

});

new Person({name: 'Tim', age: 21}).log(); // 'Tim, 21'
new Person({name: 'Chris'}).log(); // 'Chris, 0'
new Person().log(); // 'Blah, 0'

In this example we defined an explicit options property in our class declaration. What happens now is that when setOptions is called, it merges the values of the explicit options property together with the passed values from the options argument. This gives us the ability to define default values for our options—an idiom that's used widely in MooTools itself.

Finally, we close this section with a special feature of the setOptions method. If the current class implements the Events mixin together with the Options mixin, all properties passed to setOptions in the form on<Name> will be added as event handlers:

var Person = new Class({

    Implements: [Events, Options],

    initialize: function(options){
        this.setOptions(options);
    }

});

var sleep = function(){
    console.log('Sleeping'),
};

new Person({
    onSleep: sleep
});

new Person().addEvent('sleep', sleep);

Here, the two last operations are equivalent: passing an option with the name onSleep is the same as attaching an event handler for the sleep event using addEvent.

Static Members

One feature of some classical programming languages is the existence of static members. A static member is a property or method that is accessible via the class itself, but not from its instances. Revising our ooc example from Chapter 4:

Person: class {

    // Properties
    name := ""
    age  := 0

    // Methods
    init: func (name: String, age: Int) {
        this name = name
        this age = age
    }

    log: func {
        printf("%s, %d", this name, this age)
    }

    // Static Property
    count := static 0

    // Static Methods
    addPerson: static func {
        Person count += 1
    }

    getCount: static func {
        printf("Person count: %d", Person count)
    }

}

// Creating a new Person instance
mark := Person new("Mark", 23)
mark log() // "Mark, 23"

// Accessing the static method
Person addPerson()
Person getCount() // "Person count: 1"

In this example, we added three new static members to our original ooc Person class: a static property count and the static methods addPerson and getCount. Because these members are static, we can't call them using our mark instance. Instead, we called them directly from the class itself, as seen in the last lines. You'll also notice that in our addPerson and getCount methods, we didn't use the this keyword. Instead, we accessed the count property through the class identifier itself.

Because static members seem more like members of the class rather than members of the instance, they're often called class members. In fact, that is exactly what they are in languages where classes are objects, like JavaScript: they're properties and methods of the class itself. The implement method, for instance, is an example of a class member since it's called from the class itself and not through instances of the class.

Here's a reimplementation of the ooc example in JavaScript using a class:

var Person = new Class({

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
    },

    log: function(){
        console.log(this.name + ', ' + this.age);
    }

});

// Static Property
Person.count = 0;

// Static Method
Person.addPerson = function(){
    this.count += 1;
};

Person.getCount = function(){
    console.log('Person count: ' + this.count);
};

// Creating a new Person instance
var mark = new Person('Mark', 23);
mark.log();

// Accessing the static method
Person.addPerson();
Person.getCount(); // 'Person count: 1'

As in the ooc example, the count property and addPerson and getCount methods become members of the Person class itself. Because the Person class is an object, creating static members is as simple as augmenting the Person object itself. So instead of declaring our static members inside the class, we just added them to our Person class directly. You'll also notice that in the addPerson and getCount methods, we used the this keyword instead of Person to access the count property. Since the methods are added to the class itself, they becomes methods of the class—and the value of the this keyword inside the method bodies is set to the class itself.

MooTools provides a function method called extend that we could use to easily add class members:

var Person = new Class({

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
    },

    log: function(){
        console.log(this.name + ', ' + this.age);
    }

});

Person.extend({

    // Static Property
    count: 0,

    // Static Method
    addPerson: function(){
        this.count += 1;
    },

    getCount: function(){
        console.log('Person count: ' + this.count);
    }

});

// Creating a new Person instance
var mark = new Person('Mark', 23);
mark.log();

// Accessing the static method
Person.addPerson();
Person.getCount(); // 'Person count: 1'

The extend method is declared via Function.prototype and is inherited by all functions. Because a MooTools class is in essence a function, we could use the extend method for classes too. The method is similar to the implement method in that it takes an object argument with keys and values referring to members that will be added. Unlike implement, though, extend does not add the members to the prototype of the class, but to the class object itself.

Note

Don't confuse the extend method with the Extends mutator. Unlike Implements and implement, which are similar in how they work, Extends and extend perform different operations. And as I mentioned, extend is a function method inherited from Function.prototype, not Class.prototype.

Taking it one step further, we could combine what we learned from the previous sections to implement a Static mutator, so we could combine the declarations inside the class itself:

Class.Mutators.Static = function(members){
    this.extend(members);
};

var Person = new Class({

    Static: {

        // Static Property
        count: 0,

        // Static Method
        addPerson: function(){
            this.count += 1;
        },

        getCount: function(){
            console.log('Person count: ' + this.count);
        }

    },

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
    },

    log: function(){
        console.log(this.name + ', ' + this.age);
    }

});

// Creating a new Person instance
var mark = new Person('Mark', 23);
mark.log();

// Accessing the static method
Person.addPerson();
Person.getCount() // 'Person count: 1'

The Static mutator is a very simple mutator that receives an object argument containing the members that will be added to the class and invokes the extend method of the class. To use it, we simply declared a Static key inside our class with an object value containing the static members we'd like to add. The result is a cleaner and tighter class declaration.

One last thing to remember before we move on is that static or class members aren't inherited. Because they are added to the class itself rather than the class's prototype, class members are not inherited by subclasses. This is different from how real classical languages with static members work, although we can mimic the behavior by overriding the original Extends mutator:

(function() {

    var origExtends = Class.Mutators.Extends;

    Class.Mutators.Extends = function(parent){
        origExtends.call(this, parent);

        var members = {};
        for (var key in parent){
            if (parent.hasOwnProperty(key) && !this.hasOwnProperty(key)) members[key] =
parent[key];
        }
        this.extend(members);
    };

})();

We first stored the original Extends mutator function in the origExtends variable so that we can call it later. We then overrode our Class.Mutators.Extends mutator with a new function. The first line of the new mutator function calls the original mutator function in order to set the prototype chain of the class. We then loop through the members of the superclass and check for class members that we should copy into the new class. We used hasOwnProperty to check the members so that we will copy only the members that were added directly to the class and not the ones inherited from Function.prototype or from Class.prototype. Finally, we use the extend method to add the members from our superclass to our new class.

Encapsulation and Visibility

Looking back at all the classes we've created so far, you'll notice that all of them are self-contained: we defined our classes so that they have all the properties and methods they'll need. We didn't store the information about our objects in a separate location, nor did we define external functions to perform operations on our objects. Instead, we defined the properties and methods of our classes so that each instance of our class—each object—is usable on its own.

The idea of having each object contain all data (properties) and behavior (methods) it needs is an important principle in object-oriented programming called encapsulation. Encapsulation dictates that each object should be self-contained: classes should declare the properties and methods needed so that everything is contained within the object.

Related to the principle of encapsulation is the idea that while you should include all of the properties and methods needed to make your object self-contained, you should also limit what you expose of your object. In other words, you should only expose the properties and methods of your object that need to be exposed to enable other objects to effectively use your object. This principle is called visibility and it's an important part of class design.

Take, for example, the following class:

var Person = new Class({

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
        this.format();
    },

    log: function(){
        console.log(this.name + ', ' + this.age);
    },

    format: function(){
        this.name = this.name.capitalize();
        this.age = parseInt(this.age);
    }

});

// Creating a new Person instance
var mark = new Person('mark', '23'),
mark.log(); // 'Mark, 23'

mark.format(); // returns nothing

Here we have the same Person class from the previous examples but with a new method called format. This new method does two things: first, it ensures that our name property is capitalized and second, it ensures that our age property is an integer. It's called automatically from our initializer and it doesn't return anything. In other words, it's an internal method: it's used by our class to perform a necessary action, but it's not meant to be called outside of the class.

The principle of visibility tells us that since it's not needed to make the class work, our format method should not be visible outside of our object. However, we could still invoke the function as in the last line of the snippet.

Internal methods such as format that are necessary but aren't meant to be called outside the object itself must have their visibility limited so that only the object itself can access them. Most classical languages allow you to set the visibility of your members to one of three levels:

  • Public members are members that are accessible from within and from without the instance. In the example above, all our members are public.

  • Private members, on the other hand, are accessible only from within the instances of the class. Private properties can be accessed only by methods of the instance and private methods can be invoked only by other methods of the instance. Also, if a subclass inherits from a superclass with private members, instances of this subclass won't be able to access the private members of the superclass.

  • Finally, protected members are like private members: protected properties and methods can be accessed and invoked only from within the instance. However, unlike private members, protected members are also available to subclasses that inherit them.

Most popular classical languages support these three visibility levels. One notable exception, though, is the Python programming language, which supports only public members. To circumvent this "limitation," Python programmers use a generally accepted naming convention: any member that's prefixed by an underscore (e.g., _format) is considered protected. By accepting a common idiom, Python programmers are able to implement a form of style-guided visibility system.

The use of naming conventions to denote visibility can also be applied to JavaScript. Like Python, all members in JavaScript are public and it's usually the naming conventions that enable us to determine which methods are protected and which are public. In fact, you already saw one MooTools convention: the use of $ to prefix protected properties.

However, there are ways to actually mimic the behavior of private and protected methods in JavaScript. One involves using closures and the other is a nifty feature implemented by the MooTools' class system.

Private Methods

The easiest way to mimic private methods in JavaScript is to wrap your class declaration in a function and create a local function that will become your "private method":

var Person = (function(){

    // Our "private" method
    var format = function(){
        this.name = this.name.capitalize();
        this.age = parseInt(this.age);
    };

    // Our class
    return new Class({

        // Properties
        name: '',
        age: 0,

        // Methods
        initialize: function(name, age){
            this.name = name;
            this.age = age;
            format.call(this);
        },

        log: function(){
            console.log(this.name + ', ' + this.age);
        }

    });

})();
// Creating a new Person instance
var mark = new Person('mark', '23'),
mark.log(); // 'Mark, 23'

console.log(typeof mark.format); // 'undefined'

First, we used a single-execution function as an enclosure for our class declaration. Inside our enclosure, we moved our original format method to a local function of the same name. Our class declaration is also inside our enclosure, so it has access to the local function. We then changed the function invocation inside the initialize method from this.format() to format.call(this), since format is no longer a class method but a separate function. We used the call method to bind the this keyword inside our format function to the current instance. Because the format function is localized, it can't be called outside the body of the enclosure. However, when we returned the class object at the end of the function, we created a closure that retains references in our methods to the local format function. The result is that we get the same behavior as private methods: we're able to call the function from within our class, but it can't be called from outside it.

While this technique effectively mimics how private methods work, there are some drawbacks. The first is that unlike true private methods, our "private methods" aren't actually methods of the class, but external functions that are accessible only to the class because of closures. This means that we can't override them, nor can we change their implementation dynamically like real private methods.

Another drawback is that all methods that need to use the private method will have to be declared within the scope of the enclosing function. Because we employed an enclosure to localize the private function, methods that are declared from outside the enclosure also won't be able to access the function. This limits the extensibility of our class.

But perhaps the biggest drawback comes from the nature of private methods. Since they can't be accessed by inheriting classes, private methods have far too limited use. If a subclass needs the functionality of a private method from its superclass, it needs to either call a public or protected method from its superclass that has access to the method, or implement its own copy of the private method—both of which aren't appealing solutions.

Protected Methods

A better solution would be to use protected methods instead of private methods. A protected method also has limited visibility, but unlike a private method, it's accessible to inheriting subclasses. While there are times when you'd actually need to use private methods, there are more uses for protected methods in most programs you'll be building.

Unlike private methods, though, there's no simple way to implement protected methods in JavaScript. Fortunately, MooTools solves this for us by implementing its own version of protected methods:

var Person = new Class({

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
        this.format();
    },
log: function(){
        console.log(this.name + ', ' + this.age);
    },

    format: function(){
        this.name = this.name.capitalize();
        this.age = parseInt(this.age);
    }.protect()

});

// Creating a new Person instance
var mark = new Person('mark', '23'),
mark.log(); // 'Mark, 23'

mark.format(); // 'Error: The method "format" cannot be called.'

This snippet is similar to our original example where all the members are public, but you'll see that in our declaration for format, we called on the protect method of the function. When called from inside the initialize method, our format method works normally. However, when called from outside the object like we did in the last line, the format method throws an error. This effectively mimics the behavior of a protected method.

The implementation of protected methods in MooTools is divided into two parts: first is the protect method itself and the second comes from the wrapper function. The protect method is declared through Function.prototype and is inherited by all functions, but its power only becomes apparent when the method is used in conjunction with the wrapper function provided by the class system.

If you'll recall, all class methods are wrapped in a special wrapper function. If you'll look back at the code for the wrapper function in the previous chapter, you'll see that much of it involves setting the values of the current and caller variables. As we saw from the previous chapter, these variables are used to track down the currently running method for the this.parent method that's used to invoke overridden methods from the superclass.

But aside from tracking overridden methods, the wrapper function also tracks the running method (aka, the "current" method) and which method invoked the running method (aka, the "caller" method). It is also the job of the wrapper function to ensure that protected methods are callable only from within the class by checking whether the protected method was invoked by another method inside the class or whether it was invoked directly from outside the class.

To illustrate how this works, let's apply it to our previous example. When we called new Person('mark', '23'), the constructor calls our initialize method. The wrapper function for this method sets initialize as the current method. Since initialize was called directly, the value of the caller method is set to null. The initialize method then invokes the protected method format and the wrapper function for format reconfigures the variables: it sets format as the current method and initialize as the caller method. Because format is protected, the wrapper function checks whether format was called from another method inside the object. It does this by checking the value of the caller method. If the method was invoked by another method inside the object, caller will never be null. In this example, the wrapper will see that the caller is initialize, and it will execute the format method as expected.

When we call the format method directly, the same sequence of operations is performed. The wrapper function first sets format as the current method and the value of the caller method will be null, since format was called directly. Since a protected method like format can only be called by another method of the object, the wrapper function will throw an error when it sees that the value of the caller method is null.

You'll fully understand how this all works when you look at the wrapper function that we saw in the previous chapter. Right before and just after the invocation of the actual method are a set of declarations that set the values of the current and the caller methods. It is these values that enable the class system to determine not only overridden methods from the superclass but also the chain of calls leading to the current method call—which is how the class system knows whether to invoke the function or throw an error.

Like real protected methods, MooTools' protected methods are also available to inheriting classes:

var Person = new Class({

    // Properties
    name: '',
    age: 0,

    // Methods
    initialize: function(name, age){
        this.name = name;
        this.age = age;
        this.format();
    },

    log: function(){
        console.log(this.name + ', ' + this.age);
    },

    format: function(){
        this.name = this.name.capitalize();
        this.age = parseInt(this.age);
    }.protect()

});

var Mark = new Class({

    Extends: Person,

    initialize: function(){
        this.name = 'mark';
        this.age = '23';
        this.format();
    }

});

// Creating a new Person instance
var mark = new Mark();
mark.log(); // 'Mark, 23'

mark.format(); // 'Error: The method "format" cannot be called.'

Even though our format method is protected, our Mark class still has access to it since the class inherits from our original Person object. The format method still works as expected, and still throws an error if called directly.

The implementation of protected methods in MooTools—while perfect for most use cases—can be a bit weird unless you know the internals. One particular gotcha is when protected methods are used in closures:

var Thing = new Class({

    test: function(){
        var self = this;
        (function(){
            self.log();
        })();
    },

    generate: function(){
        var self = this;
        return function(){
            self.log();
        };
    },

    log: function(){
        console.log('Yes'),
    }.protect()

});

var obj = new Thing();
obj.test(); // 'Yes'

var fn = obj.generate();
fn(); // "Error: The method "log" cannot be called.'

Our class Thing has one protected method called log and two public methods: test and generate. When we call the test method, it creates a single-execution function that calls the log method. Because the single-execution function is created, invoked, and destroyed within the context of the test function, the call to the log method succeeds, since the whole procedure happens from inside the context of the method. On the other hand, generate returns a function that calls log inside its body, creating a closure. Because the procedure moves the invocation of the log method outside of the context of the generate method, the invocation fails and an error is thrown. This is because by the time that log is finally called, the generate function would have finished its execution. The call to log then would be just like directly calling log from the outside.

Because of how they're implemented, protected methods can only be called in the immediate body of other methods. This means that protected methods can't be used in callback function, event handlers, or any other function that uses closures, because the invocation gets removed from the current context. So if you need to use a method for any of these things, it's better to use a public method instead of a protected one.

Finally, we end this section with another mutator. In the section on static members, we implemented a Static mutator to simplify the process of declaring static members. To make the process of declaring protected methods easier, let's implement a corresponding Protected mutator:

Class.Mutators.Protected = function(methods){
    for (var key in methods){
        if (methods[key] instanceof Function) this.implement(key, methods[key].protect());
    }
};

As an exercise, I'll leave it to you to explain how our Protected mutator works and how to use it. This is a very easy exercise, since it's very similar to our Static mutator.

MooTools and Classes

MooTools is a class-based framework not only because it provides the best JavaScript class system currently available, but because it uses this class system itself. While other JavaScript frameworks provide some form of plug-in or extension system to add extra functionality to the framework, MooTools expects developers to use the built-in class system to do this.

Since the class system is central to MooTools, every developer who uses the framework is expected to know at least the basics of creating and extending classes. After reading this and the previous chapter, I expect that you already understand the MooTools class system deeply and I won't be surprised if you're well on your way to mastering the system.

However, knowing how to create classes and knowing how to properly design classes are wholly separate things. The first involves syntax and technique, which is easy enough to master. The second, on the other hand, involves a firm understanding of the principles and concepts of object-oriented programming, and it's a bit harder to master these.

These principles and concepts are, of course, outside the scope of this book, though we have touched on some of them, like code reuse, inheritance, encapsulation, and visibility. The only way to learn them is by reading books on object-oriented programming and design, and by using what you've learned in experimentation and in building complex JavaScript programs.

I will, however, provide you with some pointers that should come in handy:

  • Follow the MooTools style and code conventions. While coding style is mostly a personal preference, the MooTools project keeps its own set of conventions that is followed for all code that's part of the framework. All snippets from this book are written according to this convention, and a guide is included at the end of this book for reference. I suggest that you follow the official conventions even if you're writing code for personal use. Having a common style makes it easier to share and reuse code, and enables us to have a common set of idioms that we can all understand.

  • Encapsulate. Remember to make your classes self-contained. Make sure that instances of your classes can be used on their own, and be mindful of the visibility of your members.

  • If it's too big, split it. Sometimes your methods will get too complex and sometimes your classes will seem too big. If this is the case, you might be putting a lot of stuff in one place. It's better to split these items into multiple methods or multiple classes. Don't be afraid to rewrite your code and divide it into smaller logical parts.

  • Reusability is important. We create classes because we want to reuse code: instead of writing a function for each object, we create a blueprint and reuse the properties and members of that blueprint. When you see yourself doing the same thing over and over again, find a way to share a piece of code across all items that use it, like a mixin or a superclass. This is often called the DRY Principle (Don't Repeat Yourself).

  • Plan for extensibility. As your project grows, you might need to add a behavior to your classes or modify members that already exist. Plan your classes in a way that permits extensibility and change. This ensures that your classes are flexible enough to handle anything new that might come up.

  • Learn how to use design patterns. A design pattern is a particular idiom that dictates how to create common types of classes. While design patterns in general are beyond the scope of this book, you'll see several patterns used throughout the framework and I'll call your attention to them. As part of your study, read up on the common design patterns that you can use for your programs.

But perhaps the best advice I can give is to never stop learning. Read books about object-oriented programming and classical languages. Check out the code for the classes included in MooTools Core. The best way to learn the MooTools class system is to experiment and observe how MooTools does things. In fact, that's exactly what we'll do in the chapters ahead: we're going to look at the internals of core MooTools classes.

I can assure you that a lot more amazing things can be done using the features that MooTools—and the JavaScript language itself—provides.

The Wrap-Up

In this chapter, we learned more about the MooTools class system. We found out about mutators, which are class macros that enable us to transform the behavior and composition of our classes. We learned about static members and how to implement them, as well as the concepts of encapsulation and visibility and how we can use them to improve our classes.

The MooTools class system is actually part of a tandem. Its partner, called the type system, is very similar to the class system, but its use and purpose is different. In the next chapter, we'll learn all about types and native augmentation and how MooTools uses the type system to improve the features that JavaScript already provides, as well as how it adds new features of its own.

So if you're ready, put on your favorite swimwear and let's dive into the MooTools type system.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset