-
Notifications
You must be signed in to change notification settings - Fork 38
Classical Inheritance
Since augment
is primarily a classical inheritance pattern let's create some classes. We'll start by implementing the EventStream
class which is personally my favorite class:
var defclass = augment.defclass;
var EventStream = defclass({
constructor: function () {
this.argc = arguments.length;
this.argv = arguments;
this.listeners = [];
},
emit: function (event) {
var listeners = this.listeners, length = listeners.length, index = 0;
while (index < length) listeners[index++](event);
this.argc = 0;
},
addListener: function (f) {
var argc = this.argc, argv = this.argv, index = 0;
while (index < argc) f(argv[index++]);
this.listeners.push(f);
},
map: function (f) {
var events = new Events;
this.addListener(function (x) {
events.emit(f(x));
});
return events;
},
filter: function (f) {
var events = new Events;
this.addListener(function (x) {
if (f(x)) events.emit(x);
});
return events;
},
scan: function (a, f) {
var events = new Events(a);
this.addListener(function (x) {
events.emit(a = f(a, x));
});
return events;
},
merge: function (that) {
var events = new Events;
this.addListener(function (x) {
events.emit({ left: x });
});
that.addListener(function (y) {
events.emit({ right: y });
});
return events;
}
});
Now that that's out of the way let's talk about inheritance.
Event streams come in different flavors. One of the most common type of event stream is a timer stream.
A timer stream periodically emits events. It can be paused and resumed at any time. It's useful for creating animations.
var TimerStream = augment(EventStream, function (uber) {
this.constructor = function (interval) {
uber.constructor.call(this);
this.interval = interval;
};
this.start = function () {
var self = this, now = Date.now();
self.timeout = setTimeout(function () {
loop.call(self, now);
}, 0);
return now;
};
this.stop = function () {
clearTimeout(this.timeout);
return Date.now();
};
function loop(time) {
var self = this, now = Date.now(), next = time + self.interval;
self.timeout = setTimeout(function () {
loop.call(self, next);
}, next - now);
self.set(now);
}
});
As you can see creating derived classes using augment
is dead simple. Every class has a function body. Using a function as the class body has the following advantages:
- It eliminates the need for a
for..in
loop to copy the methods of the class. - It allows
augment
to easily pass values to the class (e.g.concat
anduber
). - It allows you to easily create private static data members (e.g.
loop
).
In the above TimerStream
class we use the uber
object passed to the class function body to call the base class constructor from the derived class constructor. The values passed to the class function body are:
- All the arguments after the base class and the function body passed to the
augment
function. - The prototype object of the base class. Since it's always at the end of the argument list you can leave it out if you don't need it (as we left it out in the
EventStream
class).
Using uber
you can call any method of the base class even if it's been overridden by the derived class. Here we used uber.constructor.call(this)
to call the base class constructor from the derived class.
To recap we learned the following about classical inheritance using augment
:
- Creating a derived class is as simple as passing the base class as the first argument to
augment
. - The last argument passed to the function passed to
augment
is the prototype of the base class. - The prototype of the base class can be used to call overridden base class methods.
That's all you will ever need to know about classical inheritance using augment
.