The world's smallest and fastest classical JavaScript inheritance pattern (augment
) is a seven line function which allows you to write CoffeeScript style classes with a flair of simplicity; and it still beats the bejesus out of other JavaScript inheritance libraries.
Inspired by giants like Jeremy Ashkenas and John Resig, augment
is an augmentation of ideas. Classes created using augment
have a CoffeeScript like structure, and a syntax like John Resig's; but they are more readable, intuitive and orders of magnitude faster. Plus they work on every JavaScript platform.
You can install augment
on node.js using the following npm command:
npm install augment
You can also install augment
on RingoJS using the following rp command:
rp install augment
Similarly you can install augment
for web apps using the following component command:
component install javascript/augment
You can easily include it in fiddles and benchmarks using the following HTML code:
<script src="https://raw.github.com/javascript/augment/master/lib/augment.js"></script>
Otherwise you may simply browse the source code and stick it into your program.
I am a huge follower of keeping things simple and learning by example. So let's begin:
var augment = require("augment");
var Rectangle = augment(Object, function () {
this.constructor = function (width, height) {
this.height = height;
this.width = width;
};
this.area = function () {
return this.width * this.height;
};
});
The augment
function augments objects. However if you pass it a function as the first parameter instead then it augments the prototype
of the function. It's equivalent to:
var augment = require("augment");
var Rectangle = augment(Object.prototype, function () {
this.constructor = function (width, height) {
this.height = height;
this.width = width;
};
this.area = function () {
return this.width * this.height;
};
});
The augment
function is also available as a method on Object.prototype
and Function.prototype
allowing you to alternatively write the above code as follows:
var augment = require("augment");
var Rectangle = Object.augment(function () {
this.constructor = function (width, height) {
this.height = height;
this.width = width;
};
this.area = function () {
return this.width * this.height;
};
});
Looks like normal JavaScript right? No mysterious dollar signs or dangling underscores. It's so simple that I don't even need to explain it using comments.
Now let's create our first object:
var rectangle = new Rectangle(3, 7);
console.log(rectangle.area());
That's it.
Now let's create another class which augments our first class. It's as simple as:
var Square = Rectangle.augment(function () {
this.constructor = function (side) {
Rectangle.call(this, side, side);
};
});
Now let's create an object:
var square = new Square(5);
console.log(square.area());
So simple.
What about accessing base class prototype
methods from the derived class? Let's see:
var Cube = Square.augment(function (base) {
this.constructor = function (side) {
base.constructor.call(this, side);
this.side = side;
};
this.area = function () {
return 6 * base.area.call(this);
};
this.volume = function () {
return this.side * base.area.call(this);
};
});
As you can see the second argument passed to the anonymous class body function is the prototype
of the base class Square
, which we named base
. It can be used to access the methods on the prototype
of the base class.
Also notice that instead of invoking the super class constructor as Square.call
we are using base.constructor.call
instead. Yes there's an additional property lookup but it's essentially the same.
Creating the final object:
var cube = new Cube(5);
console.log(cube.volume());
console.log(cube.area());
The module pattern in JavaScript is used to provide privacy and state via an anonymous closure. It may also optionally return an object. You may use augment
as achieve the same result. As long as you don't define this.constructor
, augment
will return a module instead of a class:
var MODULE = Object.augment(function () {
var private = true;
this.public = true;
});
You may also import values as follows:
var MODULE = Object.augment(function ($, YAHOO) {
// now have access to globals jQuery (as $) and YAHOO in this code
}, jQuery, YAHOO);
By default the prototype
of the function you are augmenting (in this case Object
) is always imported. It's passed at the end of the argument list.
The augment
framework was designed keeping code reuse in mind. Hence all the utility functions used to write the actual framework are made available to the user as well. These utilities aid in functional programming in JavaScript. They are documented below:
The bindable
function allows you to create a bindable version of an existing function which when called returns a new function bound to the given arguments. For example:
Function.prototype.defer = function () {
setTimeout.bind(null, this, 0).apply(null, arguments);
};
var deferrable = Function.bindable(Function.prototype.defer);
var deferredAlert = deferrable(alert);
deferredAlert("This will be displayed later.");
alert("This will be displayed first.");
The bindable
function is equivalent to bind.bind
(i.e. it takes a function foo
and returns another function equivalent to foo.bind
, or to put it simply bindable(foo) === foo.bind
). Hence bindable(bind)
is equivalent to bind.bind
or bindable
itself. In fact bindable
is itself is implemented as bind.bind(bind)
. Confusing? I think not.
As a thumb rule the name of the bindable version of a function should be an adjective with the suffix "able". For example a bindable bind
would be bindable
itself (which is what it actually is). A bindable call
would be callable
. A bindable apply
would be appliable
. You get my drift. Concise and descriptive names are very helpful.
The callable
function allows you to create a callable version of an existing function which when called calls the existing function with the given arguments and this
pointer. For example:
var defer = Function.callable(Function.prototype.defer);
defer(alert, "This will be displayed later.");
alert("This will be displayed first.");
To make things more clear assume that you pass a function foo
to callable
as follows - Function.callable(foo)
. This is equivalent to foo.call
(without actually calling call
). Hence Function.callable(foo)(that, arg1, ...)
is equivalent to foo.call(that, arg1, ...)
.
The Array.from
function allows you to slice an array from a start index to an end index. You can use it to create a one-level deep copy of an array or to convert an array-like object into an array. For example:
var primes = [2, 3, 5, 7];
var oddPrimes = tail(primes); // [3, 5, 7]
function tail(list) {
return arrayFrom(list, 1);
}
The ownPropertyOf
function is used to check if an object has own property. It's particularly useful if the object you're testing doesn't have the Object
constructor in its prototype chain. For example:
var object = Object.create(null);
Object.ownPropertyOf(object, "property"); // false
That's all folks!