jQuery plugin Inheritance

JavaScript is primarily a prototype-based language, and to extend jQuery with plugins, we uses prototype-based paradigm. However, it is well-known that JavaScript can be used also for prototype-oriented and even object-oriented programming (where 'oriented' stays for inheritance).

Also are actual issues about an more advanced jQuery plugin system, where plugins are entities associated to DOM elements (such as widgets). In my opinion, this is great opportunity to introduce more powerful programming paradigms to jQuery and to use them within plugins.

Here is an my try: jquery.plugin.js.

Here are some usage examples:

$.plugin('myPlugin', {
    init: function() {
        this.$el.addClass( this._opt.className );
    },
    destroy: function() {
        this.$el.removeClass( this._opt.className );
        return this._super();
    },
    getElement: function() {
        return this.el;
    }
})
.setOptions({
    className: "my_plugins"
})
.extend({
    salute: function() {
        alert( "Hi! I am plugin " + this._name_ + "!" );
    }
});

$.plugin('myPlugin2', $.myPlugin, {
    init: function() {
        this._super();
        if ( this._opt.hidden ) {
            this.$el.hide();
        }
    }
})
.setOptions({
    hidden: true
});

$.myPlugin.salute(); // => "Hi! I am plugin myPlugin!"
$.myPlugin2.salute(); // => "Hi! I am plugin myPlugin2!"

$('a').myPlugin();
$('a').myPlugin('getElement') === $('a')[0]; // => true

$('p').myPlugin2({hidden: false});
$('p').hasClass('my_plugins'); // => true
$('p').myPlugin2('destroy').hasClass('my_plugins'); // => false

Some things I would to note about this solution:

  • Plugins are classes.
  • Classes are objects (not first-class functions).
  • All of the plugins inherit from a single ancestor $.plugin.base.
  • $.plugin.base inherits $.base, a generic base class.
  • Access to overridden methods is provided with this._super() (inspired by John Resig).
  • All properties that match with /^_.*_$/ are considered special and read-only.
  • Plugins have a static special property _options_ that defines default options. The static method setOptions is used to set changes.
  • If plugin A inherits plugin B, then A._options_ inherits B._options_.

Once a plugin is instantiated, its instance contains:

  • associated DOM element (el) and respective jQuery object ($el).
  • user options combined with default options: _opt.

Object Oriented Programming

There are several benefits by adopting such OOP to plugins. Here are some:

  • Plugins can be defined by specializing other ones (plugins are classes).
  • Plugins/classes inherit class members too (classes are objects).
  • Metaprogramming - classes defines how subclasses will be generated (overriding class methods extended and extend).

Although $.plugin() would handle plugin creations and all its OOP and jQuery stuff, it can be useful to define non-plugin classes too. For that purposes the base class is exposed to be easily extended:

var Person = $.base.extended({
    init: function(name) {
        this.name = name;
    },
    presentYourself: function() {
        return "Hi. I am " + this.name + ".";
    }
});

var Employee = Person.extended({
    init: function(name, profession) {
        this._super(name);
        this.profession = profession;
    },
    presentYourself: function() {
        return this._super() + " I am a " + this.profession + ".";
    },
    offerHelp: function() {
        return "Can I help you?";
    }
})
.extend({
    fromPerson: function(person, profession) {
        profession = profession || "recruit";
        return this.create( person.name, profession );
    }
});

var p = Person.create("Robert");
p.presentYourself(); // => "Hi. I am Robert."

var e = Employee.fromPerson(p);
e.presentYourself(); // => "Hi. I am Robert. I am a recruit."
e.offerHelp(); // => "Can I help you?"

Posted in Uncategorized | Tagged , , | Leave a comment

Simple Classes in JavaScript

I made a my version of the John Resig's JavaScript Inheritance solution. The main difference is that my one doesn't impose that every 'class' have to inherit a single ancestor. This is important if you wont to inherit an already existent 'class' that is not defined in same manner.

This would be possible too:

function Person(name) {
    this.name = name;
}

Person.prototype.presentYourself = function() {
    return "Hi. I am " + this.name + ".";
}

var Employee = $class(Person, {
    constructor: function(name, profession) {
        this._super(name);
        this.profession = profession;
    },
    presentYourself: function() {
        return this._super() +
            " I am a " + this.profession;
    },
    offerHelp: function() {
        return "Can I help you?";
    }
});

Other notable difference is that constructor is used instead of init. This is because in JavaScript the constructor property is already intended to contain the prototype constructor. Also the usage of init would not be safe if the super class like Person would use it with other meaning then constructor.

Usages of the $class function can be explained with this code:

var MyClass = $class({ /* properties */ });

var SubClass = $class(MyClass, { /* properties */ });

There is also an $object function to inherit objects (great for singletons):

var universe = { /* properties */ };

var chaotic_universe = $object(universe, { /* properties */ });

Posted in Uncategorized | Tagged , | Leave a comment