Declare is a library designed to allow writing object oriented code the same way in both the browser and node.js.
##Installation
npm install declare.js
Or download the source (minified)
###Requirejs
To use with requirejs place the declare
source in the root scripts directory
define(["declare"], function(declare){
return declare({
instance : {
hello : function(){
return "world";
}
}
});
});
##Usage
declare.js provides
Class methods
as(module | object, name)
: exports the object to module or the object with the namemixin(mixin)
: mixes in an object but does not inherit directly from the object. Note this does not return a new class but changes the original class.extend(proto)
: extend a class with the given properties. A shortcut to declare(Super, {})
;
Instance methods
_super(arguments)
: calls the super of the current method, you can pass in either the argments object or an array with arguments you want passed to super_getSuper()
: returns a this methods direct super._static
: use to reference class properties and methods.get(prop)
: gets a property invoking the getter if it exists otherwise it just returns the named property on the object.set(prop, val)
: sets a property invoking the setter if it exists otherwise it just sets the named property on the object.
###Declaring a new Class
Creating a new class with declare is easy!
var Mammal = declare({
instance : {
constructor: function(options) {
options = options || {};
this._super(arguments);
this._type = options.type || "mammal";
},
speak : function() {
return "A mammal of type " + this._type + " sounds like";
},
getters : {
type : function() {
return this._type;
}
},
setters : {
type : function(t) {
this._type = t;
}
}
},
static : {
soundOff : function() {
return "Im a mammal!!";
}
}
});
You can use Mammal just like you would any other class.
Mammal.soundOff("Im a mammal!!");
var myMammal = new Mammal({type : "mymammal"});
myMammal.speak();
myMammal.get("type");
myMammal.set("type", "mammal");
myMammal.get("type");
###Extending a class
If you want to just extend a single class use the .extend method.
var Wolf = Mammal.extend({
instance: {
constructor: function(options) {
options = options || {};
this._super(arguments);
this._sound = "growl";
this._color = options.color || "grey";
},
speak : function() {
return this._super(arguments) + " a " + this._sound;
},
getters : {
color : function() {
return this._color;
},
sound : function() {
return this._sound;
}
},
setters : {
sound : function(s) {
this._sound = s;
}
}
},
static : {
soundOff : function() {
return this._super(arguments) + " that growls";
}
}
});
Wolf.soundOff();
var myWolf = new Wolf();
myWolf instanceof Mammal
myWolf instanceof Wolf
You can also extend a class by using the declare method and just pass in the super class.
var Dog = declare(Wolf, {
instance: {
constructor: function(options) {
options = options || {};
this._super(arguments);
this._sound = "woof";
},
speak : function() {
return this._super(arguments) + " thats domesticated";
}
},
static : {
soundOff : function() {
return this._super(arguments) + " but now barks";
}
}
});
Dog.soundOff();
var myDog = new Dog();
myDog instanceof Mammal
myDog instanceof Wolf
myDog instanceof Dog
var Breed = Dog.extend({
instance: {
_pitch : "high",
constructor: function(options) {
options = options || {};
this._super(arguments);
this.breed = options.breed || "lab";
},
speak : function() {
return this._super(arguments) + " with a " + this._pitch + " pitch!";
},
getters : {
pitch : function() {
return this._pitch;
}
}
},
static : {
soundOff : function() {
return this._super(arguments).toUpperCase() + "!";
}
}
});
Breed.soundOff()
var myBreed = new Breed({color : "gold", type : "lab"}),
myBreed instanceof Dog
myBreed instanceof Wolf
myBreed instanceof Mammal
myBreed.speak()
myBreed.get("type")
myBreed.get("color")
myBreed.get("sound")" //"woof"
###Multiple Inheritance / Mixins
declare also allows the use of multiple super classes.
This is useful if you have generic classes that provide functionality but shouldnt be used on their own.
Lets declare a mixin that allows us to watch for property changes.
function _set(prop, val) {
var oldVal = this.get(prop);
var ret = this._super(arguments);
this.__callHandlers(prop, oldVal, val);
return ret;
}
function _callHandlers(prop, oldVal, newVal) {
var handlers = this.__watchers[prop], l;
if (handlers && (l = handlers.length) !== 0) {
for (var i = 0; i < l; i++) {
handlers[i].call(null, prop, oldVal, newVal);
}
}
}
function _watch(prop, handler) {
if ("function" !== typeof handler) {
throw new TypeError("Invalid handler.");
}
if (!this.__watchers[prop]) {
this.__watchers[prop] = [handler];
} else {
this.__watchers[prop].push(handler);
}
}
function _unwatch(prop, handler) {
if ("function" !== typeof handler) {
throw new TypeError("Invalid handler.");
}
var handlers = this.__watchers[prop], index;
if (handlers && (index = handlers.indexOf(handler)) !== -1) {
handlers.splice(index, 1);
}
}
declare({
instance:{
constructor:function () {
this._super(arguments);
this.__watchers = {};
},
"set":_set,
__callHandlers:_callHandlers,
watch:_watch,
unwatch:_unwatch
},
"static":{
init:function () {
this._super(arguments);
this.__watchers = {};
},
"set":_set,
__callHandlers:_callHandlers,
watch:_watch,
unwatch:_unwatch
}
})
Now lets use the mixin
var WatchDog = declare([Dog, WatchMixin]);
var watchDog = new WatchDog();
function watch(id, oldVal, newVal) {
console.log("watchdog's %s was %s, now %s", id, oldVal, newVal);
}
watchDog.watch("type", watch);
watchDog.watch("color", watch);
watchDog.watch("sound", watch);
watchDog.set("type", "newDog");
watchDog.set("color", "newColor");
watchDog.set("sound", "newSound");
watchDog.unwatch("type", watch);
watchDog.unwatch("color", watch);
watchDog.unwatch("sound", watch);
watchDog.set("type", "newDog");
watchDog.set("color", "newColor");
watchDog.set("sound", "newSound");
###Accessing static methods and properties witin an instance.
To access static properties on an instance use the _static
property which is a reference to your constructor.
For example if your in your constructor and you want to have configurable default values.
consturctor : function constructor(opts){
this.opts = opts || {};
this._type = opts.type || this._static.DEFAULT_TYPE;
}
###Creating a new instance of within an instance.
Often times you want to create a new instance of an object within an instance. If your subclassed however you cannot return a new instance of the parent class as it will not be the right sub class. declare
provides a way around this by setting the _static
property on each isntance of the class.
Lets add a reproduce method Mammal
reproduce : function(options){
return new this._static(options);
}
Now in each subclass you can call reproduce and get the proper type.
var myDog = new Dog();
var myDogsChild = myDog.reproduce();
myDogsChild instanceof Dog;
###Using the as
declare
also provides an as
method which allows you to add your class to an object or if your using node.js you can pass in module
and the class will be exported as the module.
var animals = {};
Mammal.as(animals, "Dog");
Wolf.as(animals, "Wolf");
Dog.as(animals, "Dog");
Breed.as(animals, "Breed");
var myDog = new animals.Dog();
Or in node
Mammal.as(exports, "Dog");
Wolf.as(exports, "Wolf");
Dog.as(exports, "Dog");
Breed.as(exports, "Breed");
To export a class as the module
in node
Mammal.as(module);