我的JS OO如是观

来源:互联网 发布:人工智能的应用实例 编辑:程序博客网 时间:2024/05/01 05:14

关于JS的OO,这里有一份列表,均是我以前总结这方面的心得与体会,不正之处,应要提出。

  • JS OO继承、多态一法
  • JavaScript“类”继承的横向比较
  • 学习NodeJS第五天:JavaScript的继承
  • YUI 3中的继承模式及其用法简介
  • Ext.extend()中使用super关键字

纵观多少JS OO实现的方法,其内部总有些不足。我在项目试过的也有,一直以来不太满意。有什么不足和不满意?——我在过往的博文都有提了提……

本文所介绍的方法,立足于经典的JS类写法,对其扩充了重载方法(overload)之实现,而又仍然使用JSprototype原型继承的模式来处理继承的。因此,两个旧的用法(经典类写法 与 prototype继承) 与一个新实现“重载”,则“重载”为该方法的亮点,其他之外并无甚特别之处。

一、如何封装?

首先说说经典的JS类写法。本方法中采用“经典”的类写法,也只能使用这写法,其他的写法均无效。所谓“经典”写法,就是我们最开始学习js时,通常教科书跟你说的那种写法(例子一):

[javascript] view plaincopy
  1. function myClass(){  
  2.     // private local varialbies  
  3.     var localVar;  
  4.     // public variabies  
  5.     this.publicVar  = 0;  
  6.       
  7.     // private methods  
  8.     function privateMethod(a){  
  9.       
  10.     }  
  11.       
  12.     // public method  
  13.     this.publicMethod = privateMethod;  
  14. }  

而不是修改原型对象的方法:

[javascript] view plaincopy
  1. function myClass(){  
  2.     // private local varialbies  
  3.     var localVar;  
  4.       
  5.     // private methods  
  6.     function privateMethod(a){  
  7.       
  8.     }  
  9. }  
  10. myClass.prototype = {  
  11.     // public variabies  
  12.     publicVar : 0,  
  13.     // public method  
  14.     publicMethod : function (a){  
  15.       
  16.     }  
  17. };  

貌似现在越来越少人采用第一种也就是经典的写法。难道这是要突出JS“原型继承”的本质才推崇第二种写法的吗?不得而知,总之大家喜欢就是了。但在本文介绍的JS OO继承方法中,却一律采用第一种也就是经典的写法。这样子,比较像Java的类哦~呵呵。

再讲一下“例子一”部分。类myClass中,注释private local variablies所指为私有内部变量,以“var”开头则外界不可以访问,反之,通过this分配的变量或方法,则可为外界所访问。请注意this.publicMehtod = privateMethod这里,无论私有方法还是公有方法,均指向同一个函数。当然,这里举的是特例,一般我们还是拆分来写。

把代码写成类就是为了更好地封装,形成API供调用。到这里我们明确封装方式为经典的JS类写法,紧接着,就是继承。

二、如何继承?

JS只有一种继承方式,就是原型继承。设有两个类,base基类和son子类,son类是base类的派生类。这样完成了一次继承:

[javascript] view plaincopy
  1. base = new base;  
  2. son.prototype = base; // 原型继承!  

每定义一次新类,都会保存父类其实例的拷贝。父类对于子类来说,都是唯一的。上述过程非常简单,但是下面我给出的方案中,会稍微复杂一些,加入一个superObjs的数组,数组保存所有父类。代码如下:

[javascript] view plaincopy
  1. base = new base;  
  2. if(!base.superObjs){  
  3.     base.superObjs = [base];  
  4. }else{  
  5.     base.superObjs.push(base);  
  6. }  
  7. son.prototype = base; // 原型继承!  

加入superObjs的数组有什么用途?这里按下不表,权作一伏笔,后面有用。

到这里整体上依然没有任何新意。究竟我们在干什么?呵呵,当然接下来就要揭示问题的所在了。

经典封装、继承的写法有一个很大的问题,就不允许方法的重载,简单说,就是后面的类,相同名称的方法会覆盖的原来前面类相同名称的方法。覆盖的这一时刻发生在new 子类()的时候。

浮现出了这个问题,应该如何解决?还是有办法的。请见下列源码:

[javascript] view plaincopy
  1. // 访问父类的内部函数  
  2. // written by Frank Cheung  
  3. function _super(){  
  4.     var   
  5.          byExplicit = arguments[0]  
  6.         ,byExplicit = byExplicit && byExplicit.length && byExplicit.callee  
  7.         // if explicit, it means arguments.caller === arguments(old) // true!  
  8.         // if not explicit, it means get Method by arguments.caller.callee.  
  9.         ,overrided  = byExplicit ? arguments[0] : arguments.caller.callee  
  10.         ,memberName                     // 成员名称一直不变的。名称字符串其作为查询成员的凭据。  
  11.         ,superMember                    // 找到的超类成员  
  12.         ,superObjs  = this.superObjs    // 父类列表  
  13.         ,superObj                       // 当前父类(不一定是目标父类)  
  14.         ,args       = byExplicit ? Array.prototype.slice.call(arguments, 1) : arguments;  
  15.       
  16.     if(!overrided){  
  17.         debugger;  
  18.         Error.throwErr({  
  19.          msg    : "必须输入args对象!"  
  20.         ,method : arguments.callee  
  21.        });  
  22.     }  
  23.       
  24.     // 先找到成员名称。  
  25.     for(var i in this){  
  26.         if(this[i] == overrided){  
  27.             memberName = i;  
  28.         }  
  29.     }  
  30.       
  31.     for(var i = 0, j = superObjs.length; i < j; --j){  
  32.         superObj    = superObjs[j - 1];  
  33.         superMember = superObj[memberName];  
  34.           
  35.         if(superMember){  
  36.             return typeof superMember == 'function'   
  37.                         ? superMember.apply(this, args)   
  38.                         : superMember  
  39.         }  
  40.     }  
  41.     if(!superMember){  
  42.         for(var i = 0, j = superObjs.length; i < j; i++){  
  43.             superObj = superObjs[i];  
  44.             for(methodName in superObj){  
  45.                 if(superObj[methodName] == overrided){  
  46.                     superMember = superObjs[i-1][methodName];  
  47.                     return superMember.apply(this, args);  
  48.                 }  
  49.             }  
  50.         }  
  51.     }  
  52.       
  53.     throw '如果走到这一步,说明super()失败!';  
  54. }  

前面所说的“伏笔”就是为了方法重载而设的。受时间所限,今日先帖帖源码,容小弟日后再添教程详述之(有新念头,旧想法被推翻。)。

三、如何多态?

多态即多继承。然而多态的问题,我尚未作深入调查。只是知道有一点,用fn.apply/call的方法可以强制指定this对象指针,所以用来作多态亦未尝不可,即:

[javascript] view plaincopy
  1. function A_Class(){  
  2.     // private local varialbies  
  3.     var localVar;  
  4.       
  5.     // private methods  
  6.     function privateMethod(a){  
  7.       
  8.     }  
  9. }  
  10. function B_Class(){  
  11.     A_Class.call(this); // A_Class 内部的 this 变为当前 B_Class 实例。  
  12.     this.foo = function(){  
  13.       
  14.     }  
  15. }  

若说这就是“多态”,可能很多人都会觉得牵强,——我也同意,但我仍认为,这是一个比较便捷的方法,算是一个思路。但仍觉得作为真正的多态来说,还是不太贴切,有待进一步的研究吧。

附继承方法的完整代码:

[javascript] view plaincopy
  1. /** 
  2.  * 继承。 
  3.  * @param {Function} son    子类 
  4.  * @param {Function} base   父类 
  5.  */  
  6. Object.extend = (function(){  
  7.     function _super(){  
  8.         var   
  9.              byExplicit = arguments[0]  
  10.             ,byExplicit = byExplicit && byExplicit.length && byExplicit.callee  
  11.             // if explicit, it means arguments.caller === arguments(old) // true!  
  12.             // if not explicit, it means get Method by arguments.caller.callee.  
  13.             ,overrided  = byExplicit ? arguments[0] : arguments.caller.callee  
  14.             ,memberName                     // 成员名称一直不变的。名称字符串其作为查询成员的凭据。  
  15.             ,superMember                    // 找到的超类成员  
  16.             ,superObjs  = this.superObjs    // 父类列表  
  17.             ,superObj                       // 当前父类(不一定是目标父类)  
  18.             ,args       = byExplicit ? Array.prototype.slice.call(arguments, 1) : arguments;  
  19.           
  20.         if(!overrided){  
  21.             debugger;  
  22.             Error.throwErr({  
  23.              msg    : "必须输入args对象!"  
  24.             ,method : arguments.callee  
  25.            });  
  26.         }  
  27.           
  28.         // 先找到成员名称。  
  29.         for(var i in this){  
  30.             if(this[i] == overrided){  
  31.                 memberName = i;  
  32.             }  
  33.         }  
  34.           
  35.         for(var i = 0, j = superObjs.length; i < j; --j){  
  36.             superObj    = superObjs[j - 1];  
  37.             superMember = superObj[memberName];  
  38.               
  39.             if(superMember){  
  40.                 return typeof superMember == 'function'   
  41.                             ? superMember.apply(this, args)   
  42.                             : superMember  
  43.             }  
  44.         }  
  45.         if(!superMember){  
  46.             for(var i = 0, j = superObjs.length; i < j; i++){  
  47.                 superObj = superObjs[i];  
  48.                 for(methodName in superObj){  
  49.                     if(superObj[methodName] == overrided){  
  50.                         superMember = superObjs[i-1][methodName];  
  51.                         return superMember.apply(this, args);  
  52.                     }  
  53.                 }  
  54.             }  
  55.         }  
  56.           
  57.         throw '如果走到这一步,说明super()失败!';  
  58.     }  
  59.     var queue = [];  
  60.     var timer = 0;  
  61.       
  62.     function delayInherit(item){  
  63.         var son = item[0], base = item[1];  
  64.         Object.extend(son, base);  
  65.         // ok, it's done!  
  66.         debugger;     
  67. // alert(typeof base)  
  68.           
  69.     }  
  70.       
  71.     function addQueue(son, base){  
  72.         queue.push([son, base]);  
  73. //      if(!timer){  
  74. //          timer =  setTimeout(function(){  
  75. //              Array.each(queue, delayInherit, queue);  
  76. //          }, 2500 /* 这是一个模拟值,不精确 */ );  
  77. //            
  78. //      }  
  79.     }  
  80.     return function(son, base){  
  81.         // @todo if not loaded, add to queue  
  82.         if(base && typeof(base) != 'function'){  
  83.             addQueue(son, base);  
  84.             return;  
  85.         }  
  86.         base = new base;  
  87.           
  88.         if(!base._super){  
  89.             base._super = _super;  
  90.         }  
  91.           
  92.         if(!base.superObjs){  
  93.             base.superObjs = [base];  
  94.         }else{  
  95.             base.superObjs.push(base);  
  96.         }  
  97.         son.prototype = base; // 原型继承!  
  98.     }  
  99. })();