I was quite specific about what that tool should allow me to do. I wanted to:
- have namespaces that reflect the structure of my code
- store any values in my namespaces, even primitives
- load a file simply by requesting a namespace
- define dependences between different parts of code within the code itself
- not to worry about including all JavaScript files statically
- have inheritance supported by the namespaces
Sadly, I failed to find such a tool.
Luckily, I have created it and it is pretty hot. It does namespaces, dynamic loading and inheritance. The tool is called wn(), after WebNicer. Here's an overview of the most important things I felt I had to get right.
Namespaces
I have read quite a few articles about implementing namespaces in JavaScript and to be fair I haven't found one that would get it right when it comes to nested namespaces. How so? Imagine you have a namespace:
MyApplication.SomeModule
Now, let's say SomeModule represents a constructor function. Then you want to create a child constructor function which inherits after SomeModule. Acting innocently and intuitively you would probably create a child namespace for it like so:
MyApplication.SomeModule.ChildModule
Looks ok but it isn't. ChildModule just became a property of SomeModule constructor function! You didn't want that, right? All you wanted was to create a child namespace and not a property of the function you have assigned to the parent namespace.
That approach is fundamentally wrong.
I had to fix it, so the above example works just fine in wn(). Namespace object is completely separated from the value represented by that namespace. Therefore ChildModule is a child namespace of SomeModule and does not become a property of the constructor function stored in SomeModule.
Dynamic loading
I think that RequireJS is not a bad piece of software but I don't really like a loader to dictate how I should write or organize my code. I believe that the loader should be helping in the background rather than trying to change my coding preferences.
On the other hand, defining dependences between namespaces rather than files is quite tempting. By doing so you can create code which is more reusable and less prone to environmental issues. It should not mean however, that each dynamically loaded files has to define a namespace. That would be just wrong. What if you wanted to load dynamically jQuery from the Google CDN?
Closely related matter is translating namespaces to URLs. Should the namespaces reflect the directory tree or, for example, represent just the filenames? Maybe. There is no one good answer. I believe that decision should be in discretion of the developer and not necessarily the one implementing the loader.
I wanted wn() to help developers do things, not make them do things. Therefore the mapping is up to you, although the default one is included for you convenience. Dynamically loaded files don't have to define namespaces if you don't want them to. And any file can define multiple namespaces if that is your wish. Some other libraries require just one namespace per file.
Inheritance
The trickiest of them all. Obviously the language has no native mechanism for inheritance, so it needs to be implemented. Therefore there a few different approaches to inheritance in JavaScript. The two most common models are the prototypal and classical inheritance. People fight holy wars trying to argue how one is superior to the other and I strongly believe it to be pointless. They would better spend their time watching paint dry.
Both approaches have advantages and both can be used by the same code only for different things.
For main building blocks of my application I would rather use classical inheritance. I would be able to check if an object is derived from certain constructor function or whether that constructor function is present up the inheritance chain (instanceof). When the code grows, type checking becomes priceless ally of code maintainability.
On the other hand prototypal inheritance is much more useful for ad-hoc objects. One of the best examples in my opinion is default values. Imagine parent constructor's prototype with a property defaults pointing to an object with some default values. Now, in the child constructors's prototype we would like to add one more default value. We don't want to recreate the whole object, though. The easiest and cleanest thing to do - use prototypal inheritance on the defaults object.
As wn() is focused on main building blocks of an application, it supports classical inheritance. Being built around namespaces, wn() allows to express inheritance using namespaces as well. Just an extra sugar.
Again, you can find examples of inheritance on the wn() Wiki.
I hope you will find wn() useful. I made every effort to make it easy to use as well as the examples easy to read and understand. Nothing's perfect but you can help improving the library by simply dropping a line.
Thanks for reading.
Jacek
Both approaches have advantages and both can be used by the same code only for different things.
For main building blocks of my application I would rather use classical inheritance. I would be able to check if an object is derived from certain constructor function or whether that constructor function is present up the inheritance chain (instanceof). When the code grows, type checking becomes priceless ally of code maintainability.
On the other hand prototypal inheritance is much more useful for ad-hoc objects. One of the best examples in my opinion is default values. Imagine parent constructor's prototype with a property defaults pointing to an object with some default values. Now, in the child constructors's prototype we would like to add one more default value. We don't want to recreate the whole object, though. The easiest and cleanest thing to do - use prototypal inheritance on the defaults object.
As wn() is focused on main building blocks of an application, it supports classical inheritance. Being built around namespaces, wn() allows to express inheritance using namespaces as well. Just an extra sugar.
Again, you can find examples of inheritance on the wn() Wiki.
I hope you will find wn() useful. I made every effort to make it easy to use as well as the examples easy to read and understand. Nothing's perfect but you can help improving the library by simply dropping a line.
Thanks for reading.
Jacek