普通のバージョン。
Namespace("org.example.net").apply(function(ns){ return { HTTPRequest : function(){ console.log("ok");}, HTTPResponse : function(){ console.log("ok");}, }; }); Namespace("org.example.system").apply(function(ns){ return { Console : { log : function(item){console.log(item)} }, }; }); Namespace("org.example.application") .use('org.example.net *') .apply(function(ns){with(ns){ var req = new HTTPRequest; var res = new HTTPResponse; }}); Namespace("org.example.application") .use('org.example.net *') .use('org.example.system Console') .apply(function(ns){with(ns){ var req = new HTTPRequest; var res = new HTTPResponse; Console.log("ok"); }}); Namespace("org.example.application") .use('org.example.net') .use('org.example.system') .apply(function(ns){with(ns){ var req = new org.example.net.HTTPRequest; var res = new org.example.net.HTTPResponse; org.example.system.Console.log("ok"); }});
こんなふうに書きたくなったので、つくった。
var Namespace = (function(){ /* utility */ var merge = function(aObj,bObj){ for( var p in bObj ){ if( bObj.hasOwnProperty( p ) ){ aObj[p] = bObj[p]; } } return aObj; }; /* inner class of namespace*/ var _Namespace = (function(){ var nsCache = {}; var nsList = []; /* constructor*/ var Klass = function _Private_Class_Of_Namespace(fqn){ this._useList = []; this._stash = { CURRENT_NAMESPACE : fqn }; }; /* private functions */ var _loadStash = function(ns){ if(!nsCache[ns]) throw('undefined namespace:' + ns); return nsCache[ns]._stash; }; var _loadStashItem = function(ns,itemName){ var nsStash = _loadStash(ns); if(typeof(nsStash[itemName]) === 'undefined') throw('undefined item:' +ns+'#'+itemName); return nsStash[itemName]; }; var _mergeUseData = (function(){ var _loadImport = function(ns,imports){ var retStash = {}; for(var i = 0,l=imports.length;i<l;i++){ var importSyntax = imports[i]; if( importSyntax === '*' ) return _loadStash(ns); retStash[importSyntax] = _loadStashItem(ns,importSyntax); } return retStash; }; var _mergeWithNS = function(stash,ns){ var nsList = ns.split(/\./); var current = stash; for(var i = 0,l=nsList.length;i<l-1;i++){ if( !current[nsList[i]] ) current[nsList[i]] = {}; current = current[nsList[i]]; } current[nsList[nsList.length-1]] = _loadStash(ns); }; return function(stash,useData){ if( useData.import ){ merge( stash , _loadImport(useData.ns,useData.import)); }else{ _mergeWithNS(stash,useData.ns); } }; })(); var _assertValidFQN = function(fqn){ if( !fqn.match(/^[a-z][a-z0-9.]+[a-z0-9]$/) ){ throw('Invalid namespace'); } }; /* extend prototype */ (function(){ var _composeStash = function(that){ var stash = {}; for(var i = 0,l= that._useList.length;i<l;i++) _mergeUseData( stash, that._useList[i]); return merge( stash , that._stash ); }; this.use = function(){ for(var i = 0,l=arguments.length;i<l;i++){ var splittedUseSyntax = arguments[i].split(/\s/); var fqn = splittedUseSyntax[0]; var imp = splittedUseSyntax[1]; var importNames = (imp) ? imp.split(/,/): null; _assertValidFQN(fqn); this._useList.push({ ns: fqn,import: importNames }); } return this; }; this.apply = function(callback){ merge(this._stash,callback(_composeStash(this))||{}); this._useList = []; return this; }; this.define = function(obj){ return this.apply(function(){return obj}); }; }).apply(Klass.prototype); /* public interface */ return { create :function(fqn){ _assertValidFQN(fqn); if( nsCache[fqn] ){ return nsCache[fqn]; } var ns = nsCache[fqn] = new Klass(fqn); return ns; } }; })(); return function(nsString){ return _Namespace.create(nsString); }; })();