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:具體內部調用關系還是沒有弄明白,今後有時間再整理,這篇太亂了。