Friday, September 14, 2007

JavaScript namespace + using proposal

I've just uploaded inside devpro my last JavaScript proposal: a little, simple and fast namespace function, with a single static public method called using.


What's a namespace ?


A namespace is "a place where everything is unobtrusive", where in this case unobtrusive means that You can't (shouldn't) modify other libraries.


Who use a namespace ?


Many program languages (C# / Python / Java / maybe one day PHP and others) use namespace since their first implementation (packages, from - import, using).
In JavaScript world, big (but not too) libraries use namespace too (Dojo, YUI!) while many other libraries use a sort of internal namespace (jQuery, MooTools, Prototype) to separe FX, utils and other piece of code.


What about my proposal ?


It's really tiny as simple to use and You can find them in this page.


Here there's a first, basic, simple example:

// create / modify or overwrite a namespace called webreflection
// setting an object with a type key and its value
namespace("webreflection", {type:"blog"});

// create / modify or overwrite another namespace
// adding a string as value
namespace("webreflection.author", "Andrea Giammarchi");


// get created namespace
var MyBlog = namespace("webreflection");

// show saved variables
alert([MyBlog.author, MyBlog.type].join("'s "));
// Andrea Giammarchi's blog


// show just author value
alert(namespace("webreflection.author"));
// in this case Andrea Giammarchi string


Simple? You can save every kind of variable inside your namespace and You can create any kind of name, splitting them with a "." char:

var FX = namespace("this.is.my.Library.FX", function(){
// do stuff
});

var myFX = new FX;
// the same of
var myFX = new namespace("this.is.my.Library.FX");

There's something else interesting, a static global using function!


What about using ?


Function namespace.using inject your namespace scope inside a callback, sending them one or more variables:


var tellMeSomething = namespace.using("webreflection", function(){
return [
"This kind of site is a",
this.type,
"and its publisher is",
this.author
].join(" ")
});

alert(tellMeSomething);
// This kind of site is a blog and its publisher is Andrea Giammarchi


Seems interesting ? This is last example:


namespace("webreflection.utils.String", {
trim:function(str){
return str.replace(/^\s+|\s+$/g, "")
},
camel:function(str){
return str.toLowerCase().replace(/\-([a-z])/g, function(m,c){return "-"+c.toUpperCase()})
},
repeat:function(str, times){
for(var i = 0, a = new Array(times); i < times; i++)
a[i] = str;
return a.join();
}
});

alert([
namespace("webreflection.utils.String").camel("tEsT-mE"),
namespace.using("webreflection.utils.String", function(){
return this.repeat("test", 3);
}),
namespace.using("webreflection", function(){
return "[" + this.utils.String.trim(" hello ") + "]"
}),
namespace("webreflection.utils").String.repeat
].join("\n"));



So, what's new ?
Well, this way to create a dedicated namespace is the same of my old JSTONE constructor: a tested way to manage namespaces and, imho, extremely useful for every JS developer and / or library.

3 comments:

Mike (i.Skitz) said...

Hi Andrea,
First off, let me just say that it's nice to see someone else recognize the JavaScript namespace problem and attempt to solve it. Your solution is both concise and effective for the scope of what you aimed to achieve.

I'm now going to tell you about my namespace solution (Ajile), even though I can guess that you might have a lot to say about its size, considerably more than 390 bytes :-) Of course there are reasons for its ~25K file size including: 1) namespacing is only part of the problem it solves, 2) I defer code size shrinking to minification tools and 3) its implementation aims to be fully cross-browser compatible (only NS4 remains).

So far you seem to show an objective approach to assessing various JavaScript concepts and techniques, so I'm hopeful that you'll give an equally objective and insightful assessment of Ajile as well.

A little background: I initially created Ajile in 2003. At that time I named it JSPackaging. The original goal was to implement basic namespacing and dynamic script loading for Javascript. Ajile's scope has since evolved to include support for importing with aliases, flexible packaging, import callbacks, dependencies, cloaking, cache management, and MVC-style development via automatic script loading. All of these extended features have come as a result of my own use of Ajile and from user feedback, so I consider them all relevant and necessary.

As you'll see, some of the concepts you've recently written about are part of Ajile, namely dynamic script loading and namespacing. As I've said, your solution is definitely effective, but I believe it would need to be expanded to accommodate real-world usage and the expectations that accompany namespace support. Definitely let me know what you think about Ajile, objective criticism is always welcomed.

Már said...

I feel like I'm missing the point here, so bear with me.

I fail to see how this...

namespace("myNamespace", { property : 1 });
var foo = namespace.using("myNamespace", myFunc);

...is functionally any different from this:

var myNamespace = { property : 1 };
var foo = myFunc.call(myNamespace);

Andrea Giammarchi said...

màr, the point is that You can use a deep namespace or rely on other libs namespace.

Try this example instead your one:

namespace("myLib.util.DOM", {doStuff:function(){}});

namespace.using(myLib.util.DOM, callback);


The first goal is to have a common but not global object container that can be used by everyone or every library.

For example dojo.utils.FX uses a global variable while namespace("dojo.utils.FX") is always available, even if You didn't create manually dojo object.

dojo = dojo || {};
dojo.utils = dojo.utils || {};
dojo.utils.FX = dojo.utils.FX || {doStuff:function(){}};

just replace them with

namespace("dojo.utils.FX", {doStuff:function(){}});

simple, clear, less obtrusive? I hope so :-)