Working With Classes
********************

In Object-Oriented programming, the most common term you are likely to encounter is ``Class''. A class is like a blueprint for creating an object, which is an instance of that class. Classes contain members, which include primarily properties and methods. A property is a value, much like a variable, that a class ``owns''. A method, when comparing with JavaScript, is a function that is ``owned'' by a class. As a consequence, properties and methods are not part of the global scope.

JavaScript does not support classes in the manner traditionally understood by Object-Oriented programmers. This is because JavaScript follows a different model which instead uses prototypes. Using this model, JavaScript supports basic instantiation and inheritance. Rather than instantiating classes, JavaScript instantiates constructors, which are functions. The following example illustrates how you would typically create a class-like object in JavaScript:

    /**
     * Declaring "classes" WITHOUT ease.js
     */

    // our "class"
    var MyClass = function()
    {
        this.prop = 'foobar';
    }

    // a class method
    MyClass.prototype.getProp = function()
    {
        return this.prop;
    };

    // create a new instance of the class and execute doStuff()
    var foo = new MyClass();
    console.log( foo.getProp() ); // outputs "foobar"
Figure: Basic ``Class'' in JavaScript without using ease.js

This gets the job done, but the prototypal paradigm has a number of limitations amongst its incredible flexibility. For Object-Oriented programmers, it's both alien and inadequate. That is not to say that it is not useful. In fact, it is so flexible that an entire Object-Oriented framework was able to be built atop of it.

ease.js aims to address the limitations of the prototype model and provide a familiar environment for Object-Oriented developers. Developers should not have to worry about how classes are implemented in JavaScript (indeed, those details should be encapsulated). You, as a developer, should be concerned with only how to declare and use the classes. If you do not understand what a prototype is, that should be perfectly fine. You shouldn't need to understand it in order to use the library (though, it's always good to understand what a prototype is when working with JavaScript).

In this chapter and those that follow, we will see the limitations that ease.js addresses. We will also see how to declare the classes using both prototypes and ease.js, until such a point where prototypes are no longer adequate.

Declaring Classes
=================

We just took a look at what it's like declaring a class using prototypes (see f:class-js). This method is preferred for many developers, but it is important to recognize that there is a distinct difference between Prototypal and Object-Oriented development models. As an Object-Oriented developer, you shouldn't concern yourself with how a class is declared in JavaScript. In true OO fashion, that behavior should be encapsulated. With ease.js, it is.

Let's take a look at how to declare that exact same class using ease.js:

    // if client-side, use: var Class = easejs.Class;
    var Class = require( 'easejs' ).Class;

    var MyClass = Class(
    {
        'public prop': 'foobar',

        'public getProp': function()
        {
            return this.prop;
        }
    } );

    // create a new instance of the class and execute doStuff()
    var foo = MyClass();
    console.log( foo.getProp() ); // outputs "foobar"
Figure: Basic anonymous class declaration using ease.js

That should look much more familiar to Object-Oriented developers. There are a couple important notes before we continue evaluating this example:

  * The first thing you will likely notice is our use of the public keyword. This is optional (the default visibility is public), but always recommended. Future versions of ease.js may provide warnings when the visibility is omitted. We will get more into visibility later on.

  * Unlike f:class-js, we do not use the new keyword in order to instantiate our class. You are more than welcome to use the new keyword if you wish, but it is optional when using ease.js. This is mainly because without this feature, if the keyword is omitted, the constructor is called as a normal function, which could have highly negative consequences. This style of instantiation also has its benefits, which will be discussed later on.

  * ease.js's class module is imported using require() in the above example. If using ease.js client-side (see Client-Side Include), you can instead use var Class = easejs.Class. From this point on, importing the module will not be included in examples.

The above example declares an anonymous class, which is stored in the variable MYCLASS. By convention, we use CamelCase, with the first letter capital, for class names (and nothing else).

Anonymous vs. Named Classes
---------------------------

We state that f:class-easejs declared an anyonmous class because the class was not given a name. Rather, it was simply assigned to a variable, which itself has a name. To help keep this idea straight, consider the common act of creating anonymous functions in JavaScript:

    // anonymous
    var myFunc = function() {};

    // named
    function myNamedFunc() {};
Figure: Anonymous functions in JavaScript

If the function itself is not given a name, it is considered to be anonymous, even though it is stored within a variable. Just as the engine has no idea what that function is named, ease.js has no idea what the class is named because it does not have access to the name of the variable to which it was assigned.

Names are not required for classes, but they are recommended. For example, consider what may happen when your class is output in an error message.

    // call non-existent method
    foo.baz();

    // TypeError: Object #<anonymous> has no method 'baz'
Figure: Anonymous classes do not make for useful error messages

If you have more than a couple classes in your software, that error message is not too much help. You are left relying on the stack trace to track down the error. This same output applies to converting a class to a string or viewing it in a debugger. It is simply not helpful. If anything, it is confusing. If you've debugged large JS applications that make liberal use of anonymous functions, you might be able to understand that frustration.

Fortunately, ease.js permits you to declare a named class. A named class is simply a class that is assigned a string for its name, so that error messages, debuggers, etc provide more useful information. There is functionally no difference between named and anonymous classes.

    var MyFoo = Class( 'MyFoo', {} ),
        foo   = MyFoo();

    // call non-existent method
    foo.baz();

    // TypeError: Object #<MyFoo> has no method 'baz'
Figure: Declaring an empty named class

Much better! We now have a useful error message and immediately know which class is causing the issue.

Temporary Classes
-----------------

In f:class-easejs, we saw that the new keyword was unnecessary when instantiating classes. This permits a form of shorthand that is very useful for creating temporary classes, or ``throwaway`` classes which are used only once.

Consider the following example:

    // new instance of anonymous class
    var foo = Class(
    {
        'public bar': function()
        {
            return 'baz';
        }
    } )();

    foo.bar(); // returns 'baz'
Figure: Declaring a temporary (throwaway) class

In f:class-tmp above, rather than declaring a class, storing that in a variable, then instantiating it separately, we are doing it in a single command. Notice the parenthesis at the end of the statement. This invokes the constructor. Since the new keyword is unnecessary, a new instance of the class is stored in the variable FOO.

We call this a temporary class because it is used only to create a single instance. The class is then never referenced again. Therefore, we needn't even store it - it's throwaway.

The downside of this feature is that it is difficult to notice unless the reader is paying very close attention. There is no keyword to tip them off. Therefore, it is very important to clearly document that you are storing an instance in the variable rather than an actual class definition. If you follow the CamelCase convention for class names, then simply do not capitalize the first letter of the destination variable for the instance.

Temporary Instances
-------------------

Similar to Temporary Classes, you may wish to use an instance temporarily to invoke a method or chain of methods. Temporary instances are instances that are instantiated in order to invoke a method or chain of methods, then are immediately discarded.

    // retrieve the name from an instance of Foo
    var name = Foo().getName();

    // method chaining
    var car = VehicleFactory().createBody().addWheel( 4 ).addDoor( 2 ).build();

    // temporary class with callback
    HttpRequest( host, port ).get( path, function( data )
    {
        console.log( data );
    } );

    // Conventionally (without ease.js), you'd accomplish the above using the
    // 'new' keyword. You may still do this with ease.js, though it is less
    // clean looking.
    ( new Foo() ).someMethod();
Figure: Declaring a temporary (throwaway) class

Rather than storing the class instance, we are using it simply to invoke methods. The results of those methods are stored in the variable rather than the class instance. The instance is immediately discarded, since it is no longer able to be referenced, and is as such a temporary instance.

In order for method chaining to work, each method must return itself.

This pattern is useful for when a class requires instantiation in order to invoke a method. Classes that intend to be frequently used in this manner should declare static methods so that they may be accessed without the overhead of creating a new class instance.

Inheritance
===========

Inheritance is a touchy subject. It can be a powerful feature, but it can also be easily abused. Inheritance is the term used to describe the process of creating a child class that extends (inherits members from) another parent class. The parent class is also referred to as the supertype and the child is called the subtype. Let's consider the following example, where we have a dog, lazy dog, and a dog that walks on two legs:

Figure: Basic inheritance example

In the above example, we would say that LAZYDOG and TWOLEGGEDDOG are subtypes of DOG, and that DOG is the supertype of the other two. We describe inheritance as an ``is a'' relationship. That is:

  * LAZYDOG is a DOG.

  * TWOLEGGEDDOG is also a DOG.

  * DOG is not a LAZYDOG or a TWOLEGGEDDOG.

Subtypes inherit all public and protected members of their supertypes (see Member Visibility). This means that, in the case of our above example, the walk() method would be available to our subtypes. If the subtype also defines a method of the same name, as was done above, it will override the parent functionality. For now, we will limit our discussion to public members. How would we represent this class using ease.js?

    // our parent class (supertype)
    var Dog = Class( 'Dog',
    {
        'public walk': function()
        {
            // walk the dog
        }
    } );

    // subclass (child), as a named class
    var LazyDog = Class( 'LazyDog' ).extend( Dog,
    {
        'public walk': function()
        {
            // lazy dog doesn't want to walk
        }
    } );

    // subclass (child), as an anonymous class
    var TwoLeggedDog = Dog.extend(
    {
        'public walk': function()
        {
            // two-legged dog only walks on two feet, like a biped
        }
    } );
Figure: Inheritance in ease.js

You should already understand how to define a class (see Declaring Classes). The above example introduced two means of extending classes, of defining a new class that inherits from a parent:

Named Subclasses
     LAZYDOG is defined as a named subclass (see Anonymous vs. Named Classes). This syntax requires the use of Class( 'Name' ). The extend() method then allows you to extend from an existing class by passing the class reference in as the first argument.

Anonymous Subclasses
     TWOLEGGEDDOG was declared as an anonymous subclass. The syntax for this declaration is a bit more concise, but you forfeit the benefits of named classes (see Anonymous vs. Named Classes). In this case, you can simply call the supertype's extend() method. Alternatively, you can use the Class.extend( Base, {} ) syntax, as was used with the named subclass LAZYDOG.

You are always recommended to use the named syntax when declaring classes in order to provide more useful error messages. If you are willing to deal with the less helpful error messages, feel free to use anonymous classes for their conciseness.

Member Visibility
=================

One of the major hurdles ease.js aimed to address (indeed, one of the core reasons for its creation) was that of encapsulation. JavaScript's prototype model provides limited means of encapsulating data. Since functions limit scope, they may be used to mimic private members. These are often referred to as privileged members. However, declaring classes in this manner can be messy. ease.js aims to provide an elegant implementation that is both a pleasure to work with and able to support protected members.

The term visibility refers to how a class member may be accessed. There are three levels of visibility implemented by ease.js, which are listed here from most visible to least:

public
     Accessible outside of the instance (e.g. foo.publicProp). Inherited by subtypes.

protected
     Not accessible outside of the instance (only accessible by this.protectedProp). Inherited by subtypes.

private
     Not accessible outside of the instance. Not inherited by subtypes.

By default, all members are public. This means that the members can be accessed and modified from within an instance and from outside. Subtypes (classes that inherit from it) will inherit public members. Public methods expose an API by which users may use your class. Public properties, however, should be less common in practice for a very important reason, which is explored throughout the rest of this section.

Encapsulation is the act of hiding information within a class or instance. Classes should be thought of black boxes. We want them to do their job, but we do not care how they do their job. Encapsulation takes the complexity out of a situation and allows the developer to focus on accomplishing the task using familiar concepts. For example, consider a class named DOG which has a method walk(). To walk a dog, we simply call Dog().walk(). The walk() method could be doing anything. By preventing the details of the method from being exposed, we present the developer with a very simple interface. Rather than the developer having to be concerned with moving each of the dog's legs, all they have to do is understand that the dog is being walked.

Let's consider our DOG class in more detail:

    Class( 'Dog',
    {
        'private _legs': {},

        'private _body': {},

        // ...

        'public walk': function()
        {
            this.stand();
            this._moveFrontLeg( 0 );
            this._moveBackLeg( 1 );
            this._moveFrontLeg( 1 );
            this._moveBackLeg( 0 );
        },

        'protected stand': function()
        {
            if ( this.isSitting() )
            {
                // ...
            }
        },

        'public rollOver': function()
        {
            this._body.roll();
        },

        'private _moveFrontLeg': function( leg )
        {
            this._legs.front[ leg ].move();
        },

        'private _moveBackLeg': function( leg )
        {
            this._legs.back[ leg ].move();
        },

        // ...
    } );
Figure: Encapsulating behavior of a class

As you can see above, the act of making the dog move forward is a bit more complicated than the developer may have originally expected. The dog has four separate legs that need to be moved individually. The dog must also first stand before it can be walked, but it can only stand if it's sitting. Detailed tasks such as these occur all the time in classes, but they are hidden from the developer using the public API. Why should the developer be concerned with all of the legs?

As a general rule of thumb, you should use the lowest level of visibility possible unless you have a strong reason for increasing it. Start by declaring everything as private and work from there.

Private Members
---------------

Let's first explore private members. The majority of the members in the DOG class (see f:encapsulation) are private. This is the lowest level of visibility (and consequently the highest level of encapsulation). By convention, we prefix private members with an underscore. Private members are available only to the class that defined it and are not available outside the class.

    var dog = Dog();
    dog._moveFrontLeg( 1 );

    // TypeError: Object #<Dog> has no method '_moveFrontLeg'
Figure: Cannot access private members outside the class

You will notice that the dog's legs are declared private as well (see f:encapsulation). This is to ensure we look at the dog as a whole; we don't care about what the dog is made up of. Legs, fur, tail, teeth, tongue, etc - they are all irrelevant to our purpose. We just want to walk the dog. Encapsulating those details also ensures that they will not be tampered with, which will keep the dog in a consistent, predictable state.

Private members cannot be inherited. Let's say we want to make a class called TWOLEGGEDDOG to represent a dog that was trained to walk only on two feet. We could approach this in a couple different ways. The first way would be to prevent the front legs from moving. What happens when we explore that approach:

    var two_legged_dog = Class( 'TwoLeggedDog' ).extend( Dog,
    {
        /**
         * This won't override the parent method.
         */
        'private _moveFrontLeg': function( leg )
        {
            // don't do anything
            return;
        },
    } )();

    two_legged_dog.walk();
Figure: Cannot override private members of supertype

If you were to attempt to walk a TWOLEGGEDDOG, you would find that the dog's front legs still move! This is because, as mentioned before, private methods are not inherited. Rather than overriding the parent's _MOVEFRONTLEG method, you are instead defining a new method, with the name _MOVEFRONTLEG. The old method will still be called.  Instead, we would have to override the public WALK method to prevent our dog from moving his front feet.

Protected Members
-----------------

Protected members are often misunderstood. Many developers will declare all of their members as either public or protected under the misconception that they may as well allow subclasses to override whatever functionality they want. This makes the class more flexible.

While it is true that the class becomes more flexible to work with for subtypes, this is a dangerous practice. In fact, doing so violates encapsulation. Let's reconsider the levels of visibility in this manner:

public
     Provides an API for users of the class.

protected
     Provides an API for subclasses.

private
     Provides an API for the class itself.

Just as we want to hide data from the public API, we want to do the same for subtypes. If we simply expose all members to any subclass that comes by, that acts as a peephole in our black box. We don't want people spying into our internals. Subtypes shouldn't care about the dog's implementation either.

Private members (see Private) should be used whenever possible, unless you are looking to provide subtypes with the ability to access or override methods. In that case, we can move up to try protected members. Remember not to make a member public unless you wish it to be accessible to the entire world.

DOG (see f:encapsulation) defined a single method as protected - stand(). Because the method is protected, it can be inherited by subtypes. Since it is inherited, it may also be overridden. Let's define another subtype, LAZYDOG, which refuses to stand.

    var lazy_dog = Class( 'LazyDog' ).extend( Dog,
    {
        /**
         * Overrides parent method
         */
         'protected stand': function()
         {
            // nope!
            this.rollOver();
            return false;
         },
    } )();

    lazy_dog.walk();
Figure: Protected members are inherited by subtypes

There are a couple important things to be noted from the above example. Firstly, we are able to override the walk() method, because it was inherited. Secondly, since rollOver() was also inherited from the parent, we are able to call that method, resulting in an upside-down dog that refuses to stand up, just moving his feet.

Another important detail to notice is that Dog.rollOver() accesses a private property of DOG -- _BODY. Our subclass does not have access to that variable. Since it is private, it was not inherited. However, since the rollOver() method is called within the context of the DOG class, the method has access to the private member, allowing our dog to successfully roll over. If, on the other hand, we were to override rollOver(), our code would not have access to that private object. Calling this.__super() from within the overridden method would, however, call the parent method, which would again have access to its parent's private members.

Visibility Escalation
---------------------

Visibility escalation is the act of increasing the visibility of a member. Since private members cannot be inherited, this essentially means making a protected member public.

If you override a protected method, you may increase its visibility to public without any problems. If you follow the convention of prefixing private members with an underscore, you may find that it's not recommended doing so for protected members. This is because subtypes may decide to make the member public.

In order to increase the visibility, you do have to override the member. For properties, this has no discernible effect; you're just redefining it. For methods, this means that you are overriding the entire body. Therefore, you will either have to provide an alternate implementation, or call this.__super() to invoke the original method.

Note that you cannot go from public to protected. This will throw an error. You can only increase the level of visibility. This ensures that once a class defines an API, subclasses cannot alter it. That API is forever for all subtypes. This means that, if you are expecting a certain type, you can rest assured that whatever you are given, even if it is a subtype, has the API you are expecting.

Let's take a look at an example.

    var Foo = Class(
        {
            'protected canEscalate': 'baz',

            'protected escalateMe': function( arg )
            {
                console.log( 'In escalateMe' );
            },

            'public cannotMakeProtected': function()
            {
            }
        } ),

        SubFoo = Foo.extend(
        {
            /**
             * Escalating a property means redefining it
             */
            'public canEscalate': 'baz',

            /**
             * We can go protected -> public
             */
            'public escalateMe': function( arg )
            {
                // simply call the parent method
                this.__super( arg );
            }
        } );
Figure: Visibility can be escalated

Note that, in the above example, making the public CANNOTMAKEPROTECTED method protected would throw an error.