我的JS OO如是观

简介: 很久没写博客了,突然想起 JS 的 OO 自己有一个想法,并实现了,于是就拿来作新博文的内容。 关于 JS 的 OO,这里有一份列表,均是我以前总结这方面的心得与体会,不正之处,应要提出。 JS OO继承、多态一法JavaScript“类”继承的横向比较学习NodeJS第五天:JavaScript的继承YUI 3中的继承模式及其用法简介Ext.extend()中使用super关键字纵观多少 JS OO 实现的方法,其内部总有些不足。

很久没写博客了,突然想起 JS 的 OO 自己有一个想法,并实现了,于是就拿来作新博文的内容。

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

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

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

一、如何封装?

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

function myClass(){ // private local varialbies var localVar; // public variabies this.publicVar = 0; // private methods function privateMethod(a){ } // public method this.publicMethod = privateMethod; }

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

function myClass(){ // private local varialbies var localVar; // private methods function privateMethod(a){ } } myClass.prototype = { // public variabies publicVar : 0, // public method publicMethod : function (a){ } };

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

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

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

二、如何继承?

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

base = new base; son.prototype = base; // 原型继承!

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

base = new base; if(!base.superObjs){ base.superObjs = [base]; }else{ base.superObjs.push(base); } son.prototype = base; // 原型继承!

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

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

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

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

// 访问父类的内部函数 // written by Frank Cheung function _super(){ var byExplicit = arguments[0] ,byExplicit = byExplicit && byExplicit.length && byExplicit.callee // if explicit, it means arguments.caller === arguments(old) // true! // if not explicit, it means get Method by arguments.caller.callee. ,overrided = byExplicit ? arguments[0] : arguments.caller.callee ,memberName // 成员名称一直不变的。名称字符串其作为查询成员的凭据。 ,superMember // 找到的超类成员 ,superObjs = this.superObjs // 父类列表 ,superObj // 当前父类(不一定是目标父类) ,args = byExplicit ? Array.prototype.slice.call(arguments, 1) : arguments; if(!overrided){ debugger; Error.throwErr({ msg : "必须输入args对象!" ,method : arguments.callee }); } // 先找到成员名称。 for(var i in this){ if(this[i] == overrided){ memberName = i; } } for(var i = 0, j = superObjs.length; i < j; --j){ superObj = superObjs[j - 1]; superMember = superObj[memberName]; if(superMember){ return typeof superMember == 'function' ? superMember.apply(this, args) : superMember } } if(!superMember){ for(var i = 0, j = superObjs.length; i < j; i++){ superObj = superObjs[i]; for(methodName in superObj){ if(superObj[methodName] == overrided){ superMember = superObjs[i-1][methodName]; return superMember.apply(this, args); } } } } throw '如果走到这一步,说明super()失败!'; }

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

三、如何多态?

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

function A_Class(){ // private local varialbies var localVar; // private methods function privateMethod(a){ } } function B_Class(){ A_Class.call(this); // A_Class 内部的 this 变为当前 B_Class 实例。 this.foo = function(){ } }

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

附继承方法的完整代码:

/** * 继承。 * @param {Function} son 子类 * @param {Function} base 父类 */ Object.extend = (function(){ function _super(){ var byExplicit = arguments[0] ,byExplicit = byExplicit && byExplicit.length && byExplicit.callee // if explicit, it means arguments.caller === arguments(old) // true! // if not explicit, it means get Method by arguments.caller.callee. ,overrided = byExplicit ? arguments[0] : arguments.caller.callee ,memberName // 成员名称一直不变的。名称字符串其作为查询成员的凭据。 ,superMember // 找到的超类成员 ,superObjs = this.superObjs // 父类列表 ,superObj // 当前父类(不一定是目标父类) ,args = byExplicit ? Array.prototype.slice.call(arguments, 1) : arguments; if(!overrided){ debugger; Error.throwErr({ msg : "必须输入args对象!" ,method : arguments.callee }); } // 先找到成员名称。 for(var i in this){ if(this[i] == overrided){ memberName = i; } } for(var i = 0, j = superObjs.length; i < j; --j){ superObj = superObjs[j - 1]; superMember = superObj[memberName]; if(superMember){ return typeof superMember == 'function' ? superMember.apply(this, args) : superMember } } if(!superMember){ for(var i = 0, j = superObjs.length; i < j; i++){ superObj = superObjs[i]; for(methodName in superObj){ if(superObj[methodName] == overrided){ superMember = superObjs[i-1][methodName]; return superMember.apply(this, args); } } } } throw '如果走到这一步,说明super()失败!'; } var queue = []; var timer = 0; function delayInherit(item){ var son = item[0], base = item[1]; Object.extend(son, base); // ok, it's done! debugger; // alert(typeof base) } function addQueue(son, base){ queue.push([son, base]); // if(!timer){ // timer = setTimeout(function(){ // Array.each(queue, delayInherit, queue); // }, 2500 /* 这是一个模拟值,不精确 */ ); // // } } return function(son, base){ // @todo if not loaded, add to queue if(base && typeof(base) != 'function'){ addQueue(son, base); return; } base = new base; if(!base._super){ base._super = _super; } if(!base.superObjs){ base.superObjs = [base]; }else{ base.superObjs.push(base); } son.prototype = base; // 原型继承! } })();

希望我的一点点拙见,能给各位朋友一小点点帮助。不知大家是否领情,不过这里又让各路大神见笑了。

目录
相关文章
|
6天前
|
编解码 JavaScript 前端开发
js的起源
js的起源
13 1
|
JavaScript 前端开发
如何写好JS的三大原则
如何写好JS的三大原则
41 0
|
编解码 JavaScript
JS基本功系列-模块化相关小结
背景 面试中可能会被问到模块化,类似:谈谈模块化的历程?AMD和CMD的区别?这里整理一下。
62 0
|
JavaScript 前端开发 Java
JS基础-面向对象
JavaScript、Java、c#.... 面向对象:JavaScript有些区别! 类:模板 原型对象 对象:具体的实例 在JavaScript这个需要大家换一下思维方式! 原型:
75 0
|
存储 设计模式 JavaScript
JS查漏补缺——面向对象中与对象相关的知识
JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来了解面向对象中与对象相关的知识
70 0
|
JavaScript 前端开发 安全
JS查漏补缺——ECMAScript相关
JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来了解ECMAScript相关语法中的大小写和严格模式
65 0
|
缓存 自然语言处理 JavaScript
细读 JS | JavaScript 模块化之路
细读 JS | JavaScript 模块化之路
132 0
细读 JS | JavaScript 模块化之路
|
JavaScript 前端开发
细读 JS | this 详解
细读 JS | this 详解
97 0
细读 JS | this 详解
|
算法 JavaScript 前端开发
细读 JS | 相等比较详解
细读 JS | 相等比较详解
113 0
细读 JS | 相等比较详解