A “Class” function to define classes in Javascript with MooTools like syntax

Updated the 08/02/2013

I really enjoy to organize my Javascript code as objects and get profit by using inheritance.

“Approaching Javascript as Javascript and not as Java”, i read that so many times; Javascript is / can be OOP and i always thought about OOP as something good for code reusing.

I think the OOP nature of Javascript is all around the prototypal chain; a constructor function is the only way to define objects that achives these two points:

  1. members on the prototypal chain
  2. the constructor property and the instance of operator working as intended

What i am going to introduce here is a function Class that uses another syntax to achieve the same, native result than the constructor functions; i am avoiding closures as class pattern because they don’t store members on the prototype (but they are a good way to define private members).

I think that the Class function makes the syntax less redundant and verbose, moving the focus from the function to the prototype. Also, it provides tools for code reusing: Javascript has native inheritance but not native tools to use it.
A more expressive syntax to write objects in OOP approach, as Class function tries to provide, is in my opinion a good way to help the development.

The Class function:

Goals

  • Encourage objective Javascript instead of sequential programming (!)
  • MooTools syntax compatibility (means be allowed to move the code on MooTools or project like moo4q in the future)
  • Get an unique class-pattern (avoiding mixed code like using prototype and closures patterns togheter)
  • Moving the focus from the function to the prototype, for the functions constructor modelling
  • Get lighter classes by provides tools for code reusing
  • Class powered by really light code, easy to be mantained by me (or you!)
  • Basic functionalities from MooTools: extend, implement, parent method, the function “initialize” as constructor (and as minor: setOptions)

About performance

  • At class generation time: the class generation’s code runs only once, to create the class; no performance decreasing
  • Using Extends, functions on the sub are wrappers of the super’s functions with the same name. The functions stack grows with it!

Quick demo here

Github page to download sources here

How to use it:

Download the file class-min.js from scr-min/ here and include it in your pages; call it in this way:

var MyClass = new Class({ /* properties */ });

The keywords for Class are: Extends, Implements ; the function initialize is called as constructor of the class. Inside methods, for classes using Extends, you can call this.parent(/*args*/) to call the corrispondent super class method.

Check the following examples to know more.

Class Example

var Cat = new Class({
    name: "",
    initialize: function(name) {
        this.name = name;
    }
});
var myCat = new Cat('Micia');
alert(myCat.name); // alerts 'Micia'

Extends Example

var Animal = new Class({
    age: -1,
    initialize: function(age) {
        this.age = age;
    }
});
var Cat = new Class({
    Extends: Animal,
    name: "",
    initialize: function(name, age) {
        this.parent(age); // calls initalize method of Animal
        this.name = name;
    }
});
var myCat = new Cat('Micia', 20);
alert(myCat.name); // alerts 'Micia'
alert(myCat.age); // alerts 20
alert(myCat.constructor == Animal); // alerts true
alert(myCat instanceof Cat); // alerts true
alert(myCat instanceof Animal); // alerts true

Implements Example

var Animal = new Class({
    age: -1,
    initialize: function(age) {
        this.age = age;
    },
    setAge: function(age) {
        alert("setAge from Animal");
        this.age = age;
    }
});
var Cat = new Class({
    Implements: Animal,
    // overriding: no parent method for Implement!
    setAge: function(age) {
        alert("setAge from Cat");
        this.age = age
    }
});
var myCat = new Cat();
myCat.setAge(30); // alerts "setAge from Cat"
alert(myCat.constructor == Cat); // alerts true
alert(myCat instanceof Cat); // alerts true
alert(myCat instanceof Animal); // alerts false

I see it better in this example:

var DIE = function() { 
    throw new Error("Method not implemented");
};
var Animal = new Class({
    age: -1,
    setAge: function(age) { DIE(); },
    die: function() { DIE(); }
});
// Cat has an implementation for the method "setAge"
// but not for the method "die"
var Cat = new Class({
    Implements: Animal,
    setAge: function(age) {
        this.age = age
    }
});
var myCat = new Cat();
myCat.setAge(30);
alert(myCat.age); // alerts 30
// throws the error "Method not implemented"
// and stops the execution
myCat.die();

Objects with the same name on sub and super are merged

var Animal = new Class({
    options: {
        sex: null,
        kind: null
    },
    initialize: function(age){
        this.age = age;
    }
});
var Cat = new Class({
    Extends: Animal,
    options: {
        kind: "cat",
        favouriteFood: "fish"
    },
    initialize: function(name, age){
        // it calls initalizes method of Animal class
        this.parent(age);
        this.name = name;
    }
});
// check your console: 
// {sex: null, kind: cat, favouriteFood: fish}
Cat.prototype.options;

Properties, as objects or array, are cloned for every instance

When you define a class using Class, the properties as objects or arrays (special case for functions follows) are cloned for every instance, they are not taken from the prototype chain:

var Animal = new Class({
    attributes: ["sex", "kind"]
});
var myPuppy = new Animal(),
    myOtherPuppy = new Animal();

// in pure Javascript, the following alerts would print true;
// they print false using Class

alert(myPuppy.array === Animal.prototype.array) //alerts false
alert(myOtherPuppy === Animal.prototype.array) // alerts false
alert(myPuppy.array === myOtherPuppy.array) // alerts false

// one more example:
// the prototypal chain is not involved with
// arrays (here) and objects

myPuppy.attributes[0] = "new value";
alert(myPuppy.attributes); // alerts "new value","kind"
alert(myOtherPuppy.attributes); // alerts "sex","kind"
alert(Animal.prototype.attributes); // alerts "sex","kind"

As you can see, three different arrays are instantiated. About functions, thay are taken from the prototype chain but that is not true when a class extends another class and they have a method defined using the same name:

var Animal = new Class({
    sayHi: function(){ // not the best example for Animal...
        alert("Hi!");
    }
});
var Cat = new Class({
    Extends: Animal,
    sayHi: function(){}
});

// "Extends" not involved

var myCat = new Animal(),
    myOtherCat = new Animal();
alert(myCat.sayHi === myOtherCat.sayHi &&
      myOtherCat.sayHi === Animal.prototype.sayHi) // alerts true
myCat.sayHi.ciao = "ciao";
// alerts "ciao ciao"
alert(myOtherCat.sayHi.ciao + " " + Animal.prototype.sayHi.ciao);

// "Extends" involved

var myCat = new Cat(),
    myOtherCat = new Cat();
alert(myCat.sayHi === myOtherCat.sayHi); // alerts false
alert(myOtherCat.sayHi === Animal.prototype.sayHi); // false
alert(myCat.sayHi === Animal.prototype.sayHi); // alerts false
myCat.sayHi.ciao = "ciao";
// alerts "undefined undefined"
alert(myOtherCat.sayHi.ciao + " " + Animal.prototype.sayHi.ciao);

What happens unsing “Extends” is that functions are wrappers to the super’s methods (that is needed to use the “parent” method).

Private stuff Example

var Cat;
(function() {
    var myPrivateProperty = "ciao";
    Cat = new Class({
        initialize: function(name){
            this.name = name;
        },
        getMyPrivateProperty: function() {
            return myPrivateProperty;
        }
    });
})();
var myCat = new Cat("puppy");
alert( myCat.getMyPrivateProperty() ); // alerts "ciao!"
// error: myPrivateProperty is not defined
alert( myPrivateProperty );

Ok… i really don’t like that too but it works, it is a start!

To do

  • ย The setOptions method is missed at the moment, i think it is really useful, inside the constructor, in a lots of situations.
  • As minor, classes from MooTools get these properties on their prototype:
    parent, $constructor
    The same properties are missed, or there are but with a different behaviour, using my Class function
  • The same as above is for the instances of a class defined by MooTools: they get these property (missed or not the same behaviour using mine):
    $caller, caller, $constructor, parent

Conclusions

I think this code could be interesting for who is using jQuery or any other toolkit / framework without any Class definition tools… or it can be just an interesting case study!

Foot notes

Implement and Extend functions are designed to extend the native object Function’s prototype; if you don’t like to use the Class function you can do that:

Function.prototype.extend = window.InheritanceApi.Extend;
Function.prototype.implement = window.InheritanceApi.Implement;

var Super = function(){};
Super.prototype.sayHi = function(){ alert("Hi!"); };

var Sub = function(){},
    Sub2 = function(){};
Sub.extend(  Super );
Sub2.implement( Super );

var test = new Sub(),
    test2 = new Sub2();
test.sayHi(); // alerts Hi!
test2.sayHi(); // alerts Hi!

Some links

Thanks for reading!

Advertisements
This entry was posted in Javascript, jQuery, My code, Programming and tagged , , , , , , . Bookmark the permalink.

7 Responses to A “Class” function to define classes in Javascript with MooTools like syntax

  1. serkan says:

    Very usefull Inheritance tool.Thanks!

    Just try to run the code below:

    var testClass = new Class({
    testTwo: function () {
    },
    test: function (i) {
    console.log(“parent ” + i)
    }
    });
    var childTest = new Class({
    Extends: testClass,
    testTwo: function () {
    },
    test: function (i) {
    console.log(i);
    this.parent(i);
    }
    });
    var x = new childTest ();
    x.test(“call”);

    It won’t call the parent method. But if you move the “test” method to top (before the “testTwo”) in the parent class, it runs as expected.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s