github: https://github.com/hirokidaichi/namespace-js
Define the Namespace object:
var Namespace
Now let’s take a closer look at the definition of the Namespace object, which is a NamespaceDefinition object. This object is a function object (the constructor of the NamespaceDefinition object, if no parameters are given, a main namespace will be generated by default), and there are three attributes, Object, Definition, and Proc. Its values are NamespaceObjectFactory, NamespaceDefinition, and createProcedure function object classes.
196 var createNamespace = function(fqn){
197 return new NamespaceDefinition(
198 NamespaceObjectFactory.create(fqn || 'main')
199 );
200 };
201 merge(createNamespace, {
202 'Object' : NamespaceObjectFactory ,
203 Definition: NamespaceDefinition,
204 Proc : createProcedure
205 });
NamespaceObjectFactory: Generate NamespaceObject object based on fqn.
The NamespaceObjectFactory object has only one create method, and the parameter is the name of the namespace (Fully Qualified Name). This method has a closure environment, which has a cache variable used to cache all generated NamespaceObject objects.
A NamespaceObject object contains three attributes, stash (record the current namespace), fqn (namespace name), proc (createProcedure object). Methods include: enqueue, call, valueof, merge, getStash, getExport
74 var NamespaceObject = function _Private_Class_Of_NamespaceObject(fqn){
75 merge(this, {
76 stash: { CURRENT_NAMESPACE : fqn },
77 fqn : fqn,
78 proc : createProcedure()
79 });
80 };
81 merge(NamespaceObject.prototype, {
82 enqueue: function(context) {
83 this.proc.next(context);
84 },
85 call: function(state,callback) {
86 this.proc.call(state, callback);
87 },
88 valueOf: function() {
89 return "#NamespaceObject<" this.fqn ">";
90 },
91 merge: function(obj) {
92 merge(this.stash,obj);
93 return this;
94 },
95 getStash: function() {
96 return this.stash;
97 },
98 getExport: function(importName) {
99 if (importName === '*') return this.stash;
100
101 var importNames = importName.split(/,/),
102 retStash = {};
103 for(var i = 0,l=importNames.length;i104 retStash[ importNames[i] ] = this.stash[ importNames[i] ];
105 }
106 return retStash ;
107 }
108 });
109 var NamespaceObjectFactory = (function() {
110 var cache = {};
111 return {
112 create :function(fqn ){
113 _assertValidFQN(fqn);
114 return (cache[fqn] || (cache[fqn] = new NamespaceObject(fqn)));
115 }
116 };
117 })();
NamespaceDefinition:
This object includes 5 properties, namespaceObject, requires, useList, stash, defineCallback. And provides related methods: use, _mergeStashWithNS, loadImport, define, getStash, valueOf, apply
This object is the core of the namespace library, which provides all required methods.
During initialization, add a function object to the steps queue of NamespaceObject's proc, and the function will call the apply method.
119 var NamespaceDefinition = function _Private_Class_Of_NamespaceDefinition(nsObj) {
120 merge(this, {
121 namespaceObject: nsObj,
122 requires : [],
123 useList : [],
124 stash : {},
125 defineCallback : undefined
126 });
127 var _self = this;
128 nsObj.enqueue(function($c){ _self.apply($c); });
129 };
130 merge(NamespaceDefinition.prototype, {
131 use: function(syntax){
132 this.useList.push(syntax);
133 var splitted = syntax.split(/s /);
134 var fqn = splitted[0];
135 var importName = splitted[1];
136 _assertValidFQN(fqn);
137 this.requires.push(function($c){
138 var context = this;
139 var require = NamespaceObjectFactory.create(fqn);
140 require.call(this,function(state){
141 context.loadImport(require,importName);
142 $c();
143 });
144 });
145 return this;
146 },
147 _mergeStashWithNS: function(nsObj){
148 var nsList = nsObj.fqn.split(/./);
149 var current = this.getStash();
150
151 for(var i = 0,l=nsList.length;i152 if( !current[nsList[i]] ) current[nsList[i]] = {};
153 current = current[nsList[i]];
154 }
155
156 var lastLeaf = nsList[nsList.length-1];
157 current[lastLeaf] = merge(current[lastLeaf] || {}, nsObj.getStash());
158 },
159 loadImport: function(nsObj,importName){
160 if( importName ){
161 merge( this.stash, nsObj.getExport(importName) );
162 }else{
163 this._mergeStashWithNS( nsObj );
164 }
165 },
166 define: function(callback){
167 var nsDef = this, nsObj = this.namespaceObject;
168 this.defineCallback = function($c) {
169 var ns = {
170 provide : function(obj){
171 nsObj.merge(obj);
172 $c();
173 }
174 };
175 merge(ns, nsDef.getStash());
176 merge(ns, nsObj.getStash());
177 callback(ns);
178 };
179 },
180 getStash: function(){
181 return this.stash;
182 },
183 valueOf: function(){
184 return "#NamespaceDefinition<" this.namespaceObject "> uses :" this.useList.join(',');
185 },
186 apply: function(callback){
187 var nsDef = this;
188 createProcedure(nsDef.requires)
189 .next(nsDef.defineCallback)
190 .call(nsDef,function(){
191 callback( nsDef.getStash() );
192 });
193 }
194 });
createProcedure:该对象是一个函数对象,返回Procedure对象的next方法的结果。
Procedure:
Procedure对象有三个属性:state,steps,_status(默认为init)。提供一下几种方法:next,isRunnig,enqueue,dequeue,call,_invoke。
1 var Namespace = (function(){
2 /* utility */
3 var merge = function(target, source){ // Copy all the properties of source to target Go and return target object 4 for(var p in source)
5 if(source.hasOwnProperty( p )) target[p] = source[p];
6 return target;
7 };
8 var _assertValidFQN = function(fqn){ // Verify the validity of the namespace name, which must be lowercase alphanumeric, underscore and dot
9 if(!(/^[a-z0-9_.] /) .test(fqn)) throw('invalid namespace');
10 };
11
12 var Procedure = function _Private_Class_Of_Proc(){
13 merge(this, {
14 state : {}, // Status
15 steps : [], // Array to store status
16 _status: 'init'
17 });
18 };
19 merge( Procedure.prototype, {
20 next: function(state){ // If state has a value, store it at the end of the steps queue, and then return this
21 if(state) this.enqueue(state);
22 return this;
23 },
24 isRunning: function(){ // Determine whether it is running status
25 return (this._status === 'running');
26 },
27 enqueue: function(state){ // Here, the array steps is actually used to simulate the queue, and enqueue is put into the queue from the end of the queue
28 this.steps.push(state);
29 } ,
30 dequeue: function(){ // dequeue means dequeuing from the opposite end
31 return this.steps.shift();
32 },
33 call: function(initialState,callback) { //
34 if( this.isRunning() ) throw("do not run twice");
35
36 this.state = initialState || {}; // Save the current state (NamespaceDefinition Object)
37 this.enqueue(function($c){ // Function is queued steps
38 $c(); // Execute the function passed in
39 if(callback)callback(this) ; // If there is a callback function, execute the callback function
40 });
41 this._status = 'running'; // Set the status to running
42 this._invoke(); // Call _invoke
43 },
44 _invoke: function(){
45 var _self = this;
46 var step = _self.dequeue(); // Object dequeue (FIFO)
47 if( !step ){
48 _self._status = 'finished'; // If the queue is empty, set the status to finished
49 return;
50 } // When step is an array Don’t follow this path
51 if( step.call ) { // If the object is a function object, execute the call method, and specify the internal this as _self.state, and continue calling_ in the callback function invoke
52 return step.call( _self.state,function _cont(state){
53 if( state ) _self.state = state;
54 _self._invoke();
55 }) ;
56 }
57 var finishedProcess = 0;
58 if( step.length === 0 ) _self._invoke(); // If the length of the array is 0, call _invoke
59 for(var i =0,l=step.length;i60 step[i].call(_self.state,function _joinWait(){
61 finishedProcess ;
62 if( finishedProcess == l ){
63 _self. _invoke();
64 }
65 });
66 }
67 }
68 });
69
70 var createProcedure = function(state) {
71 return new Procedure().next(state);
72 };
73
74 var NamespaceObject = function _Private_Class_Of_NamespaceObject(fqn){
75 merge(this, {
76 stash: { CURRENT_NAMESPACE : fqn },
77 fqn : fqn,
78 proc : createProcedure()
79 });
80 };
81 merge(NamespaceObject.prototype, {
82 enqueue: function(context) {
83 this.proc.next(context);
84 },
85 call: function(state,callback) {
86 this.proc. call(state, callback);
87 },
88 valueOf: function() {
89 return "#NamespaceObject<" this.fqn ">";
90 },
91 merge: function(obj) {
92 merge(this.stash,obj);
93 return this;
94 },
95 getStash: function() {
96 return this .stash;
97 },
98 getExport: function(importName) {
99 if (importName === '*') return this.stash;
100
101 var importNames = importName.split(/,/),
102 retStash = {};
103 for(var i = 0,l=importNames.length;i104 retStash[ importNames[ i] ] = this.stash[ importNames[i] ];
105 }
106 return retStash;
107 }
108 });
109 var NamespaceObjectFactory = (function() {
110 var cache = {};
111 return {
112 create :function(fqn){
113 _assertValidFQN(fqn);
114 return (cache[fqn] || (cache [fqn] = new NamespaceObject(fqn)));
115 }
116 };
117 })();
118
119 var NamespaceDefinition = function _Private_Class_Of_NamespaceDefinition(nsObj) {
120 merge(this, {
121 namespaceObject: nsObj,
122 requires : [],
123 useList : [],
124 stash : {},
125 defineCallback : undefined
126 });
127 var _self = this;
128 nsObj.enqueue(function($c){ _self.apply($c); });
129 };
130 merge(NamespaceDefinition.prototype, {
131 use: function(syntax){ // Use namespace
132 this.useList.push(syntax); // The namespace string is stored in the array useList
133 var splitted = syntax.split(/s /); // Namespace and its objects are separated by spaces
134 var fqn = splitted[0]; // Get namespace
135 var importName = splitted [1]; // Get the object in namespace
136 _assertValidFQN(fqn);
137 this.requires.push(function($c){ // Put a function into the requires array
138 var context = this;
139 var require = NamespaceObjectFactory.create(fqn); // Get the specified NamespaceObject object. Previously generated objects can be obtained directly from the cache
140 require.call(this,function(state ){ // Call the call method of the NamespaceObject object
141 context.loadImport(require,importName);
142 $c();
143 });
144 });
145 return this;
146 },
147 _mergeStashWithNS: function(nsObj){
148 var nsList = nsObj.fqn.split(/./);
149 var current = this.getStash() ;
150
151 for(var i = 0,l=nsList.length;i152 if( !current[nsList[i]] ) current[nsList[ i]] = {};
153 current = current[nsList[i]];
154 }
155
156 var lastLeaf = nsList[nsList.length-1];
157 current[lastLeaf] = merge(current[lastLeaf] || {}, nsObj.getStash());
158 },
159 loadImport: function(nsObj,importName){
160 if( importName ) {
161 merge( this.stash, nsObj.getExport(importName) );
162 }else{
163 this._mergeStashWithNS( nsObj );
164 }
165 },
166 define: function(callback){
167 var nsDef = this, nsObj = this.namespaceObject;
168 this.defineCallback = function($c) { // Assign a value to defineCallback and define the callback function context, two objects, nsDef and nsObj.
169 var ns = {
170 provide : function(obj){
171 nsObj.merge(obj);
172 $c();
173 }
174 };
175 merge(ns, nsDef.getStash());
176 merge(ns, nsObj.getStash());
177 callback(ns);
178 };
179 } ,
180 getStash: function(){
181 return this.stash;
182 },
183 valueOf: function(){
184 return "#NamespaceDefinition<" this.namespaceObject " > uses :" this.useList.join(',');
185 },
186 apply: function(callback){
187 var nsDef = this;
188 createProcedure(nsDef. requires)
189 .next(nsDef.defineCallback)
190 .call(nsDef,function(){
191 callback( nsDef.getStash() );
192 });
193 }
194 });
195
196 var createNamespace = function(fqn){
197 return new NamespaceDefinition(
198 NamespaceObjectFactory.create(fqn || 'main')
199 );
200 };
201 merge(createNamespace, {
202 'Object' : NamespaceObjectFactory,
203 Definition: NamespaceDefinition,
204 Proc : createProcedure
205 });
206 return createNamespace;
207 })();
Additionally define the methods supported by Namespace:
Namespace.use
Namespace.fromInternal
Namespace.GET
Namespace.fromExternal
1 Namespace.use = function(useSyntax){ return Namespace().use(useSyntax); }
2 Namespace.fromInternal = Namespace.GET = (function(){
3 var get = (function(){
4 var createRequester = function() {
5 var xhr;
6 try { xhr = new XMLHttpRequest() } catch(e) {
7 try { xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {
8 try { xhr = new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {
9 try { xhr = new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {
10 try { xhr = new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {
11 throw new Error( "This browser does not support XMLHttpRequest." )
12 }
13 }
14 }
15 }
16 }
17 return xhr;
18 };
19 var isSuccessStatus = function(status) {
20 return (status >= 200 && status < 300) ||
21 status == 304 ||
22 status == 1223 ||
23 (!status && (location.protocol == "file:" || location.protocol == "chrome:") );
24 };
25
26 return function(url,callback){
27 var xhr = createRequester();
28 xhr.open('GET',url,true);
29 xhr.onreadystatechange = function(){
30 if(xhr.readyState === 4){
31 if( isSuccessStatus( xhr.status || 0 )){
32 callback(true,xhr.responseText);
33 }else{
34 callback(false);
35 }
36 }
37 };
38 xhr.send('')
39 };
40 })();
41
42 return function(url,isManualProvide){
43 return function(ns){
44 get(url,function(isSuccess,responseText){
45 if( isSuccess ){
46 if( isManualProvide )
47 return eval(responseText);
48 else
49 return ns.provide( eval( responseText ) );
50 }else{
51 var pub = {};
52 pub[url] = 'loading error';
53 ns.provide(pub);
54 }
55 });
56 };
57 };
58 })();
59
60 Namespace.fromExternal = (function(){
61 var callbacks = {};
62 var createScriptElement = function(url,callback){
63 var scriptElement = document.createElement('script');
64
65 scriptElement.loaded = false;
66
67 scriptElement.onload = function(){
68 this.loaded = true;
69 callback();
70 };
71 scriptElement.onreadystatechange = function(){
72 if( !/^(loaded|complete)$/.test( this.readyState )) return;
73 if( this.loaded ) return;
74 scriptElement.loaded = true;
75 callback();
76 };
77 scriptElement.src = url;
78 document.body.appendChild( scriptElement );
79 return scriptElement.src;
80 };
81 var domSrc = function(url){
82 return function(ns){
83 var src = createScriptElement(url,function(){
84 var name = ns.CURRENT_NAMESPACE;
85 var cb = callbacks[name];
86 delete callbacks[name];
87 cb( ns );
88 });
89 }
90 };
91 domSrc.registerCallback = function(namespace,callback) {
92 callbacks[namespace] = callback;
93 };
94 return domSrc;
95 })();
96
97 try{ module.exports = Namespace; }catch(e){}
具体看一个例子:
1 Namespace('logtest')
2 .define(function (ns) {
3 console.log(2);
4 ns.provide({
5 log : function () { console.log(3); }
6 });
7 });
8
9 console.log(4);
10
11 Namespace
12 .use('logtest')
13 .apply( function (ns) {
14 console.log(5);
15 ns.logtest.log();
16 });
1:Namespace('logtest') => new NamespaceDefinition(NamespaceObjectFactory.create('logtest'))
即生成一个NamespaceDefinition对象,该对象是由NamespaceObject对象来初始化的,该对象同时还有三个属性,Object,Definition,Proc。其值依次为 NamespaceObjectFactory,NamespaceDefinition,createProcedure函数对象类。Namespace('logtest') 返回的结果就是生成的NamespaceDefinition对象,然后调用其define方法,初始化defineCallback。此时仅仅是定义,不做具体的动作。
166 define: function(callback){
167 var nsDef = this, nsObj = this.namespaceObject;
168 this.defineCallback = function($c) { // 给defineCallback赋值,同时定义一下该回调函数的上下文,nsDef和nsObj两个对象。
169 var ns = {
170 provide : function(obj){
171 nsObj.merge(obj);
172 $c();
173 }
174 };
175 merge(ns, nsDef.getStash());
176 merge(ns, nsObj.getStash());
177 callback(ns);
178 };
179 },
2: Use the objects in the previously defined namespace, Namespace.use() => Namespace().use() => Namespace('main').use(). Initialize the requirements array when calling use.
131 use: function(syntax){ / / Use namespace
132 this.useList.push(syntax); // The namespace string is stored in the array useList
133 var splitted = syntax.split(/s /); // Namespace and its objects are used Separate by spaces
134 var fqn = splitted[0]; // Get namespace
135 var importName = splitted[1]; // Get objects in namespace
136 _assertValidFQN(fqn);
137 this.requires.push(function($c){ // Put a function into the requires array
138 var context = this;
139 var require = NamespaceObjectFactory.create(fqn); // Get the specified NamespaceObject Object, previously generated objects can be obtained directly from the cache
140 require.call(this,function(state){ // Call the call method of the NamespaceObject object
141 context.loadImport(require,importName);
142 $c();
143 });
144 });
145 return this;
146 },
3: Call main’s apply method. Take a look at the specific apply method
186 apply: function(callback ){
187 var nsDef = this;
188 createProcedure(nsDef.requires)
189 .next(nsDef.defineCallback)
190 .call(nsDef,function(){
191 callback ( nsDef.getStash() );
192 });
193 }
Take out the objects in the requires array to generate a Proc object, and put the objects in the requires array into the queue steps , nsDef.defineCallback is also queued (undefined in this example), and the call method of Proc is called. The first parameter is nsDef, and the second parameter is the callback function.
Specific usage:
When defining Namespace, all definitions are placed in define and defined in the form of anonymous functions. function (ns) { // Specific implementation // Define an object that is exposed to the outside world. After using the nsName, the external party can call it through ns.nsName.key() ns.provide({ key : value});}
The advantage of using namespace.js is that all definitions are only executed when needed, that is, the definitions are parsed only when they are actually used.
ps: The specific internal calling relationship is still not clear. I will sort it out when I have time in the future. This article is too messy.