enchant.js | Classes

Mithat Konar

Classes

Prototypal object orientation

In enchant.js | Fundamentals, we created a rain-collection game in which we created game elements with the help of factory functions. The factory functions (one of which is reproduced below) returned Sprite instances with some extra properties and some additional things preconfigured.


  

The above is an example of an approach to object oriented programming called prototypal object orientation. JavaScript was designed to be prototypal.

In the simplest kind of prototypal object orientation, you first create an instance of an object that is close to what you need and then you modify that instance as needed for your purposes. The additions and modification happen after the root object is created. The factory function above does exactly that. (A factory function is just a function that returns a new object instance.)

You can think of the droplet returned by makeDroplet(collector) as being a special kind of Sprite—one that:

When you call the makeDroplet(collector) factory function, the function creates a Sprite instance, makes the necessary modifications to that instance, and then returns the modified instance.

You don't need to put the Sprite instantiation and subsequent modification inside a function. The process would still be prototypal if you didn't. The reason we put it all into a function was to make the code clearer and more modular.

Class-based object orientation

There is another kind of object-oriented programming called class-based object orientation. Many popular object-oriented programming languages (e.g., C++, Java, C#) are class-based rather than prototypal, so many people are more comfortable working in a class-based paradigm rather than a prototypal one. JavaScript normally doesn't support class-based object orientation, but the designers of enchant.js have built basic support for class-based object orientation into the framework.

The underlying idea of class-based object orientation is that rather than create an object instance and modify it after you create it, you write definitions for classes of objects that define what an instance of an object of that class should be from the start—sort of like writing a blueprint. The "blueprint" is a class definition. When you instantiate the object from the "blueprint" (using the new operator in JavaScript) it's ready to go.

Inheritance

But there's a little more to it.

As far as our droplets are concerned, we have already discussed how they are a specialized kind of Sprite—kind of like how a Subaru Forester 2.5i Premium is a specialized kind of Subaru Forester, which is itself a specialized kind of SUV, which is itself a specialized kind of vehicle.

Let's assume that someone has already created a "blueprint" for the Subaru_Forester. Given that a Subaru_Forester_2.5i_Premium (a class of object) is a specialized kind of Subaru_Forester (another class of object), a really easy way to create a "blueprint" for a Subaru_Forester_2.5i_Premium would be to say something like: Subaru_Forester_2.5i_Premium is a Subaru_Forester that:

Once we have the made the new "blueprint", we can start to make instances of Subaru_Forester_2.5i_Premium.

When you define things in this way, that is, creating a base "blueprint" and then using additional "blueprints" to show only how more specialized versions are different from the base, you are using inheritance. Inheritance is a feature of class-based object orientation. We would say that Subaru_Forester_2.5i_Premium inherits from Subaru_Forester. All the things that are in the definition of a Subaru_Forester also go for a Subaru_Forester_2.5i_Premium, except for the things you have added to changed in the Subaru_Forester_2.5i_Premium definition.

If you were to take a class-based view of a droplet in our program, you would view a single droplet as an instance of the Droplet class and that Droplet is a specialized kind of Sprite (i.e., the Droplet class inherits from the Sprite class.)

In the class definition, you would want to indicate that Droplet inherits from Sprite and then define how a Droplet differs from a Sprite (i.e., what additions and modifications are needed).

The key difference between protoypal thinking and class-based thinking is that protoypal thinking takes a make-and-modify approach whereas class-based thinking takes a what-is-it approach. It's really just a small shift in perspective.

Example

In the code below, we have rewritten the prototypal makeDroplet(collector) factory function as an equivalent class definition. The syntax,

var Droplet = Class.create(Sprite, ...

says that Droplet is a class that inherits from the Sprite class—meaning that a Droplet is a special kind of Sprite. The initialize method is run automatically when you create an instance (which we will show you how to do in a bit). Inside the initialize method, the JavaScript keyword this is used to mean "this instance".


  

To create an instance of the Droplet class using enchant.js' class, you would write something like:

var myDroplet = new Droplet(collector);

Neither prototypal nor class-based object oriention can be considered superior to the other. When it comes to programming in enchant.js, whether you use prototypal or class-based object orientation is mostly a matter of taste.

Complete game

For completeness, here is the complete game rewritten to use classes rather than the prototypal factory functions.

Download project


    

Run