namespace.js Javascript的命名空间库


Posted in Javascript onOctober 11, 2011

github:https://github.com/hirokidaichi/namespace-js
定义Namespace对象:
var Namespace
现在来具体看一下Namespace对象的定义,它是一个NamespaceDefinition对象。该对象是一个函数对象(NamespaceDefinition对象的构造函数,如果不给参数的话就默认生成一个main的命名空间),还有三个属性,Object,Definition,Proc。其值依次为NamespaceObjectFactory,NamespaceDefinition,createProcedure函数对象类。

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:根据fqn生成NamespaceObject对象。
NamespaceObjectFactory对象只有一个create方法,参数就是命名空间的名字(Fully Qualified Name)。该方法有一个闭包环境,该环境里有一个cache变量用于缓存所有生成的NamespaceObject对象。
一个NamespaceObject对象包含有三个属性,stash(记录当前namespace),fqn(namespace名字),proc(createProcedure对象)。方法包括: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;i<l;i++){ 
104 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:
该对象包括5个属性,namespaceObject,requires,useList,stash,defineCallback。并提供了相关的方法:use,_mergeStashWithNS,loadImport,define,getStash,valueOf,apply
该对象是namespace库的核心,它提供了所有需要的方法。
初始化的时候,在NamespaceObject的proc的steps队列里追加一个函数对象,该函数将调用apply方法。
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;i<l-1;i++){ 
152 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){ // 把source的所有自身属性copy到target中去,并返回target对象 4 for(var p in source) 
5 if(source.hasOwnProperty( p )) target[p] = source[p]; 
6 return target; 
7 }; 
8 var _assertValidFQN = function(fqn){ // 验证namespace名字的有效性,必须是小写英数字,下划线和点 
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 : {}, // 状态 
15 steps : [], // 存储状态的数组 
16 _status: 'init' 
17 }); 
18 }; 
19 merge(Procedure.prototype, { 
20 next: function(state){ // 如果state有值,存入到steps队列队尾,然后返回this 
21 if(state) this.enqueue(state); 
22 return this; 
23 }, 
24 isRunning: function(){ // 判定是否为running状态 
25 return (this._status === 'running'); 
26 }, 
27 enqueue: function(state){ // 这里其实是用数组steps来模拟队列,enqueue就是从队尾入队列 
28 this.steps.push(state); 
29 }, 
30 dequeue: function(){ // dequeue就是从对头出队列 
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 || {}; // 保存当前state(NamespaceDefinition对象) 
37 this.enqueue(function($c){ // 函数入队列steps 
38 $c(); // 执行传进来的函数 
39 if(callback)callback(this); // 如果存在回调函数的,执行回调函数 
40 }); 
41 this._status = 'running'; // 设置状态为running 
42 this._invoke(); // 调用_invoke 
43 }, 
44 _invoke: function(){ 
45 var _self = this; 
46 var step = _self.dequeue(); // 对象出队列(FIFO) 
47 if( !step ){ 
48 _self._status = 'finished'; // 如果队列为空,则设置状态为finished 
49 return; 
50 } // step是数组的情况下不走这条路径 
51 if( step.call ) { // 如果该对象是函数对象的时候,则执行该call方法,并指定内部this为_self.state,回调函数里面继续调用_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(); // 如果该数组长度为0,则调用_invoke 
59 for(var i =0,l=step.length;i<l;i++){ // 针对数组里的所有对象(函数对象)调用各自的call方法,并指定各自函数的this为_self.state对象(nsDef对象) 
60 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;i<l;i++){ 
104 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){ // 使用namespace 
132 this.useList.push(syntax); // 该namespace字符串存入数组useList 
133 var splitted = syntax.split(/\s+/); // namespace和它的对象使用空格分开 
134 var fqn = splitted[0]; // 取得namespace 
135 var importName = splitted[1]; // 取得namespace中的对象 
136 _assertValidFQN(fqn); 
137 this.requires.push(function($c){ // 放一个函数到requires数组中 
138 var context = this; 
139 var require = NamespaceObjectFactory.create(fqn); // 获取指定的NamespaceObject对象,之前生成过得对象可以直接从缓存中获取 
140 require.call(this,function(state){ // 调用NamespaceObject对象的call方法 
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;i<l-1;i++){ 
152 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) { // 给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 }, 
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 })();

追加定义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:使用之前定义的namespace里面的对象,Namespace.use() => Namespace().use() => Namespace('main').use()。调用use的时候初始化requires数组。
131 use: function(syntax){ // 使用namespace 
132 this.useList.push(syntax); // 该namespace字符串存入数组useList 
133 var splitted = syntax.split(/\s+/); // namespace和它的对象使用空格分开 
134 var fqn = splitted[0]; // 取得namespace 
135 var importName = splitted[1]; // 取得namespace中的对象 
136 _assertValidFQN(fqn); 
137 this.requires.push(function($c){ // 放一个函数到requires数组中 
138 var context = this; 
139 var require = NamespaceObjectFactory.create(fqn); // 获取指定的NamespaceObject对象,之前生成过得对象可以直接从缓存中获取 
140 require.call(this,function(state){ // 调用NamespaceObject对象的call方法 
141 context.loadImport(require,importName); 
142 $c(); 
143 }); 
144 }); 
145 return this; 
146 },

3:调用main的apply方法。看一下具体的apply方法
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 }

取出requires数组里面的对象生成Proc对象,并把该requires数组里的对象入队列steps中,还有nsDef.defineCallback也入队列(此例中为undefined),调用Proc的call方法。第一个参数是nsDef,第二个参数是回调函数。
具体使用方法:
在定义Namespace的时候,所有的定义放在define里面,并且以匿名函数的形式定义。function (ns) {

// 具体的实现

// 定义对外公开的对象,外部在use该nsName之后,就可以通过ns.nsName.key()来调用它

ns.provide({ key : value});}
使用namespace.js的好处,所有的定义都是在需要的时候才开始执行的,也就是在具体使用的时候才开始解析定义。

ps:具体内部调用关系还是没有弄明白,今后有时间再整理,这篇太乱了。

Javascript 相关文章推荐
jQuery判断复选框是否勾选的原理及示例
May 21 Javascript
随鼠标移动的时钟非常漂亮遗憾的是只支持IE
Aug 12 Javascript
浅析js预加载/延迟加载
Sep 25 Javascript
HTML5canvas 绘制一个圆环形的进度表示实例
Dec 16 Javascript
微信小程序 flex实现导航实例详解
Apr 26 Javascript
javascript变量提升和闭包理解
Mar 12 Javascript
微信小程序methods中定义的方法互相调用的实例代码
Aug 07 Javascript
vue elementUI tree树形控件获取父节点ID的实例
Sep 12 Javascript
JavaScript 反射和属性赋值实例解析
Oct 28 Javascript
JS使用Chrome浏览器实现调试线上代码
Jul 23 Javascript
解决vue中axios设置超时(超过5分钟)没反应的问题
Sep 04 Javascript
vue实现两个组件之间数据共享和修改操作
Nov 12 Javascript
基于jquery的当鼠标滚轮到最底端继续加载新数据思路分享(多用于微博、空间、论坛 )
Oct 10 #Javascript
jquery.tmpl JQuery模板插件
Oct 10 #Javascript
brook javascript框架介绍
Oct 10 #Javascript
jQuery 一个图片切换的插件
Oct 09 #Javascript
跨域请求之jQuery的ajax jsonp的使用解惑
Oct 09 #Javascript
多浏览器兼容性比较好的复制到剪贴板的js代码
Oct 09 #Javascript
jquery利用event.which方法获取键盘输入值的代码
Oct 09 #Javascript
You might like
实用函数10
2007/11/08 PHP
基于PHP文件操作的详解
2013/06/05 PHP
PHP检测用户语言的方法
2015/06/15 PHP
PHP实现的同步推荐操作API接口案例分析
2016/11/30 PHP
javascript 学习之旅 (1)
2009/02/05 Javascript
javascript写的一个链表实现代码
2009/10/25 Javascript
jquery.validate的使用说明介绍
2013/11/12 Javascript
全面解析Bootstrap表单使用方法(表单按钮)
2015/11/24 Javascript
Angular2利用组件与指令实现图片轮播组件
2017/03/27 Javascript
解决VUEX刷新的时候出现数据消失
2017/07/03 Javascript
vue 2.0封装model组件的方法
2017/08/03 Javascript
如何优雅地在Node应用中进行错误异常处理
2019/11/25 Javascript
基于ts的动态接口数据配置的详解
2019/12/18 Javascript
node.js中对Event Loop事件循环的理解与应用实例分析
2020/02/14 Javascript
Node.js Domain 模块实例详解
2020/03/18 Javascript
javascript利用键盘控制小方块的移动
2020/04/20 Javascript
[03:14]辉夜杯主赛事 12月25日每日之星
2015/12/26 DOTA
使用python实现省市三级菜单效果
2016/01/20 Python
Python 3.x 安装opencv+opencv_contrib的操作方法
2018/04/02 Python
使用Python设计一个代码统计工具
2018/04/04 Python
Python线程下使用锁的技巧分享
2018/09/13 Python
PyQt5响应回车事件的方法
2019/06/25 Python
python gdal安装与简单使用
2019/08/01 Python
Python 将json序列化后的字符串转换成字典(推荐)
2020/01/06 Python
使用Django清空数据库并重新生成
2020/04/03 Python
Python抓包并解析json爬虫的完整实例代码
2020/11/03 Python
美国隐形眼镜销售网站:ContactsDirect
2017/10/28 全球购物
STAY JAPAN台湾:预订日本民宿
2018/07/22 全球购物
J2EE面试题集锦(附答案)
2013/08/16 面试题
终端业务员岗位职责
2013/11/27 职场文书
学术会议欢迎词
2014/01/09 职场文书
韩国商务邀请函
2014/01/14 职场文书
年度考核登记表个人总结
2015/03/06 职场文书
2015民办小学年度工作总结
2015/05/26 职场文书
初婚未育证明样本
2015/06/18 职场文书
教师旷工检讨书
2015/08/15 职场文书