Organise Your Code With base2.Packages
A base2.Package provides a mechanism for bundling classes, constants and functions within a closure. You can define what symbols you want to export from the Package and you can define the symbols you want to import into the closure.
Here is the template for creating a base2 Package:
new function(_) { // create the closure // create the package object var MyPackage = new base2.Package(this, { name: "MyPackage", version: "1.0", imports: "SomeOtherPackage", exports: "MY_CONSTANT,MyClass,myFunction" }); // evaluate the imported namespace eval(this.imports); // define package contents var MY_CONSTANT = 42; var MyClass = SomeOtherClass.extend({ // class definition }); function myFunction() { return "Hello!"; }; // evaluate the exported namespace (this initialises the Package) eval(this.exports); };
By running the above code we will have created a package that lives on the base2 object:
base2.MyPackage // => "[base2.MyPackage]" base2.MyPackage.MY_CONSTANT // => 42 base2.MyPackage.myFunction() // => "Hello!"
The base2.MyPackage namespace is initialised by the call eval(this.exports)
. You must make this call at the end of the closure that contains your package objects.
Referring to base2.MyPackage.MY_CONSTANT
is a tad verbose so base2 Packages have a special property (namespace
) which defines all of the symbols that the package exports. The namespace
property for our example above looks like this:
print(base2.MyPackage.namespace); // => var MyPackage=base2.MyPackage;var MY_CONSTANT=MyPackage.MY_CONSTANT;var MyClass=MyPackage.MyClass;var myFunction=MyPackage.myFunction;
We can now evaluate the namespace
property to gain access to the Package properties as if they were defined in the current scope:
eval(base2.MyPackage.namespace); myFunction() // => "Hello!" MY_CONSTANT // => 42
You can eval
the namespace
property in the global scope to globalise the contents of your Package. Or you can perform the eval
within a closure to keep the global namespace clean. This is the mechanism that allows us to import the namespaces of other packages into your own package. That is what the call to eval(this.imports)
in the example above provides. The namespaces of the base2 and JavaScript packages are automatically imported into any package and you don’t need to define them in the imports
property.
Here’s a fully fledged example:
new function(_) { // create the closure // create the package object var shapes = new base2.Package(this, { name: "shapes", version: "1.0", exports: "PI,Circle,Rectangle" }); // evaluate the imported namespace eval(this.imports); var PI = 3.14; var Shape = Base.extend({ constructor: function(x, y) { this.move(x, y); }, x: 0, y: 0, getArea: Undefined, move: function(x, y) { this.x = Number(x); this.y = Number(y); } }); var Circle = Shape.extend({ constructor: function(x, y, radius) { this.base(x, y); this.radius = Number(radius); }, radius: 0, getArea: function() { return PI * Math.pow(this.radius, 2); } }); var Rectangle = Shape.extend({ constructor: function(x, y, width, height) { this.base(x, y); this.width = Number(width); this.height = Number(height); }, width: 0, height: 0, getArea: function() { return this.width * this.height; } }); // evaluate the exported namespace (this initialises the Package) eval(this.exports); };
Here’s another example that imports the Package we declared above:
new function(_) { // create the closure // create the package object var graphics = new base2.Package(this, { name: "graphics", version: "1.0", imports: "shapes", exports: "Layout" }); // evaluate the imported namespace eval(this.imports); // we can refer to the Rectangle class directly because we have imported the // shapes Package. var Layout = Rectangle.extend({ // I don't know anything about graphics }); // evaluate the exported namespace (this initialises the Package) eval(this.exports); };
I hope that I’ve made a reasonable job of explaining how base2 Packages work. All of the base2 code is organised into packages. By importing and exporting namespaces you end up with more readable code. File sizes are smaller too.
Comments (8)
Leave a comment
Comment: #1
Thanks Dean,
As first glance I was a bit confused until I realized Packages were a way to add plugins to base2. Great work.
Comment: #2
Seems like a very clean API – but isn’t eval evil?
I like “import Package as Alias” which I use as “var Alias = base2.Package”. base2 looks very impressive.
Comment: #3
[…] His latest installment is on base2 packages: A base2.Package provides a mechanism for bundling classes, constants and functions within a closure. You can define what symbols you want to export from the Package and you can define the symbols you want to import into the closure. […]
Comment: #4
[…] His latest installment is on base2 packages: A base2.Package provides a mechanism for bundling classes, constants and functions within a closure. You can define what symbols you want to export from the Package and you can define the symbols you want to import into the closure. […]
Comment: #5
This is genius. Now that I’ve read up on ES4’s namespaces, I think I could appreciate some in ES3.
Great job on this one.
@daaku:
eval
has its valid uses, and this (importing into the local scope) is one of them.Comment: #6
For the longest time I could not wrap my brain around your “new function(_) { … };” constructs used here, until I recently realized it was half of the “new function(_) { … }(this);” construct, meant to pass the global object to the scope in a _ variable (right?). I’d suggest either losing the _, add the (this), and/or mention what the _ argument is somewhere in the post.
Comment: #7
@Johan – don’t break your brain trying to understand
the new function(_)
syntax. I use this to provide access to an undocumented feature of packer. Basically, variables defined in a closure with this signature will not be processed by packer. That’s all. No magic.Comment: #8
[…] Array-Funktionen kann ich somit schon mal weglassen, ebenfalls können wir die schöne base2.Package-Funktion nutzen. Dies ermöglicht, dass alle Funktionen in einem eigenen Namespace sind. […]
Comments are closed.