In each example, the object literal style is shown on the left, and the metaconstructor style is shown on the right.
// object literal
var ExampleThing = decl({
//...
});
// "metaconstructor"
var ExampleThing = decl(function(){
//...
});
There's no need to provide a constructor
function
in your declaration; it will automatically be created if omitted.
var Thing = decl({
title: "thing",
describe: function(){
alert("Looks like a " + this.title + ".");
}
});
var Thing = decl(function(){
this.title = "thing";
this.describe = function(){
alert("Looks like a " + this.title + ".");
};
});
var item = new Thing();
item.describe(); // "Looks like a thing."
item.title = "glass sculpture";
item.describe(); // "Looks like a glass sculpture."
You may provide an explicit constructor
function
in your declaration; it will be returned by decl
.
var Animal = decl({
species: "unknown critter",
constructor: function(species) {
if (species) {
this.species = species;
}
},
describe: function(){
alert("Looks like a " + this.species);
}
});
var Animal = decl(function(){
this.species = "unknown critter";
this.constructor = function(species) {
if (species) {
this.species = species;
}
};
this.describe = function(){
alert("Looks like a " + this.species);
};
});
var donald = new Animal("duck");
donald.describe(); // "Looks like a duck"
decl
makes prototypal inheritance easy.
The metaconstructor style (right) offers a clean syntax for inheritance.
var Dog = decl({
"decl-data": {extend: Animal},
species: "dog"
});
var Dog = decl(function(){
this.extend(Animal);
this.species = "dog";
});
var rex = new Dog();
rex.describe(); // "Looks like a dog"
In the previous example, no constructor
was provided
in the declaration, so decl
created a wrapper for
the parent constructor. Arguments may be passed to the child constructor
just as they are passed to the parent constructor.
var fido = new Dog("hound");
fido.describe(); // "Looks like a hound"
If you provide a constructor
in the declaration,
you may want to apply
the parent constructor.
Calling this.extend
within a metaconstructor
returns a reference to the parent constructor's prototype.
var Toad = decl({
"decl-data": {extend: Animal},
species: "toad",
constructor: function(species, habitat) {
// apply the parent constructor
Animal.apply(this, arguments);
if (habitat) {
this.habitat = habitat;
}
}
});
var Toad = decl(function(){
var parent = this.extend(Animal);
this.species = "toad";
this.constructor = function(species, habitat) {
// apply the parent constructor
parent.constructor.apply(this, arguments);
if (habitat) {
this.habitat = habitat;
}
};
});
Declarations can be amended like this:
// more properties for Animal.prototype
decl({
"decl-data": {augment: Animal},
sound: "makes no sound",
makeNoise: function() {
alert("The " + this.species + " " + this.sound);
}
});
// more properties for Animal.prototype
decl(function(){
this.augment(Animal);
this.sound = "makes no sound";
this.makeNoise = function() {
alert("The " + this.species + " " + this.sound);
};
});
var critter = new Animal();
critter.sound = "growls";
critter.makeNoise(); // "The unknown critter growls"
By default, decl
stores information in a property
of the declaration object named decl-data
.
The metaconstructor style abstracts this; there is no reason
to worry about decl-data
when using the metaconstructor style.
When using the object literal style, metadata must be stored in the decl-data
property. The property name may be changed from decl-data
to
something else by setting decl.dataKey
to a different value.
This may be desirable if the object literal style is preferred.
Note that the decl-data
property will never be copied
from the declaration object into the prototype of the constructor,
whether or not the property is renamed.
// "decl-data" is annoying to type, but shouldn't conflict with anything.
var Frog = decl(function(){
"decl-data": {extend: Animal},
species: "frog"
});
// Change decl's data storage property name.
decl.dataKey = "meta";
// This is easier to type. Your constructor prototypes can't have a property named "meta" now though.
var Gopher = decl(function(){
meta: {extend: Animal},
species: "gopher"
});
// Change storage property name back to default.
decl.dataKey = "decl-data";
Take a look at the unit tests for more examples.