namespace.js Javascript的命名空间库

  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:具体内部调用关系还是没有弄明白,今后有时间再整理,这篇太乱了。