zoukankan      html  css  js  c++  java
  • Javascript的命名空间库namespace.js

    命名空间的好处已经耳熟能详,但是JS中并没有原生态支持命名空间,这个库就是为js提供命名空间的功能。

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

  • 相关阅读:
    网页对ipad iphone 的样式支持
    Url Properties of SPSite and SPWeb
    vs 2010 中添加 代码模板,动态生成创建时间,创建作者等信息
    明确工作目标,提升工作效率的利器Momentum插件
    与父亲相处的无奈
    VS2012一打开就停止工作的解决方法
    关于SQL Server 中连接查询Join的几种常见用法
    CSS零基础学习笔记.
    SQL Server2014,附加数据库失败,错误为:5120的解决方法
    C#Abstract抽象类的语法
  • 原文地址:https://www.cnblogs.com/foxracle/p/2195629.html
Copyright © 2011-2022 走看看