javascript 原型模式实现OOP的再研究


Posted in Javascript onApril 09, 2009


function A() 
{ 
this.v1 = 10; 
} 
A.prototype.print = function() 
{ 
alert(this.v1); 
} 
function B() 
{ 
} 
B.prototype = new A(); 
new B().print();

运行这段代码输出是10,看起来好像是类B继承了类A的方法print,并产生了正确的输出,实际上的执行流程是在类B生成的对象中,不能直接得到方法print,于是在它的prototype属性中查找对应的方法,这个方案的出发点很好,类B中存在就调用类B的,否则调用它的prototype属性(类A)中的同名方法。然而有时候我们需要子类的方法调用父类同名的方法,比如类B改为
function B() 
{ 
this.v2 = 15; 
} 
B.prototype = new A(); 
B.prototype.print = function() 
{ 
this.prototype.print.call(this); 
alert(this.v2); 
} 
new B().print();

其中,this.prototype.print就是类A对应的print方法,输出是10和15,好像解决了问题,实际上不然,我们再继承一层
function C() 
{ 
this.v3 = 20; 
} 
C.prototype = new B(); 
C.prototype.print = function() 
{ 
this.prototype.print.call(this); 
alert(this.v3); 
} 
new C().print();

我们期待的输出依次是10, 15, 20, 但是很不幸,这样写的结果是系统会陷入死循环
因为在执行这个方法时,
C.prototype.print = function() 
{ 
this.prototype.print.call(this); 
alert(this.v3); 
}

将会循环的调用以下方法,直到堆栈溢出
B.prototype.print = function() 
{ 
this.prototype.print.call(this); 
alert(this.v2); 
}

正确的写法此时应该变为
B.prototype.print = function() 
{ 
A.prototype.print.call(this); 
alert(this.v3); 
} 
C.prototype.print = function() 
{ 
B.prototype.print.call(this); 
alert(this.v3); 
}

但是在继承关系发生了改变的情况下,需要改动相当多的对父类的引用,这也不是最佳的办法,在实际应用中,可以考虑使用_super来代替父类的名称,_this来代替自身的名称,然后用一个标准的方法将他们替换成[super].prototype或者[this].prototype,从而没有歧义的调用指定的方法,这才是javascript的OOP的真正解决方案,相关的代码如下:
/* 
在使用OOP继承体系时, 首先要定义类, 最后执行extendsOf初始化类, 使用_super引用父类, 如, 使用_this引用本身的方法, 
例如: 
function Extend2() 
{ 
_super(); 
} 
Extend2.prototype.setValue = function(value) 
{ 
_super.setValue(value); 
alert("Extend2:" + value); 
} 
Extend2.extendsOf(Extend1);

类继承树的根为Object. 注意: 所有使用了转义的成员函数都必须定义在extendsOf方法调用之前.
对象可以设定一个自动运行的初始化代码, 以下划线开头, 名称与对象名称相同, 如
Object._Object = function() {...}
如果对象的初始化代码不存在, 将自动寻找父对象的初始化代码, 直到全部查找完毕
Function.FRBlock = / *("([^"^\\]|\\")*"|'([^'^\\]|\\')*'|\/([^\/^\\]|\\.)*\/) */; 
Function.FRSpace = /\s+/g; 
Function.FRSign = / ?(^|;|:|<|>|\?|,|\.|\/|\{|\}|\[|\]|\-|\+|\=|\(|\)|\*|\^|\%|\|) ?/g; 
Function.FRRefer = /_(super|this)(\.[^(]+)?\(([^\)]*)\)/; 
Function.prototype.FCompile = function(name) 
{ 
//检查是类的构造函数还是类的属性, name参数为空表示是构造函数 
if (name) 
{ 
//类的属性不是函数实现, 直接赋值到子类后退出 
if (typeof this.prototype[name] != "function") 
{ 
window[this.FClassName].prototype[name] = this.prototype[name]; 
return; 
} 
var s = this.prototype[name].toString(); 
} 
else 
{ 
var s = this.toString(); 
} 
var b = ""; 
var r; 
//过滤空白字符 
while (r = Function.FRBlock.exec(s)) 
{ 
s = RegExp.rightContext; 
b += RegExp.leftContext.replace(Function.FRSpace, " ").replace(Function.FRSign, "$1") + r[1]; 
} 
b += s.replace(Function.FRSpace, " ").replace(Function.FRSign, "$1"); 
var i = b.indexOf("("); 
var j = b.indexOf(")", i); 
if (!name) 
{ 
this.FClassName = b.substring(9, i); 
} 
var cn = this.FClassName; 
var arg = b.substring(i + 1, j); 
s = b.substring(j + 2, b.length - 1); 
b = ""; 
//进行调用转义, 将_super,_this替换为指定的方法 
for (var n = 0; r = Function.FRRefer.exec(s); n++) 
{ 
if (r[2]) 
{ 
if (!name && !n) 
{ 
b = this.FSuperClass.FClassName + ".apply(this,arguments);"; 
} 
r[2] = ".prototype" + r[2]; 
} 
else if (r[1] == "this") 
{ 
//JS函数不区分参数的差异, 构造函数不允许递归调用自身 
throw "Constructor call mustn't be \"_this();\" in a constructor"; 
} 
else if (name || RegExp.leftContext) 
{ 
throw "Constructor call must be the first statement in a constructor"; 
} 
else 
{ 
r[2] = ""; 
} 
s = RegExp.rightContext; 
b += RegExp.leftContext + (r[1] == "this" ? cn : this.FSuperClass.FClassName) + r[2] + (r[3] ? ".call(this," + r[3] + ")" : ".apply(this,arguments)"); 
} 
if (n) 
{ 
b += s; 
} 
else if (name) 
{ 
//没有针对_this,_super的调用, 不用编译 
window[cn].prototype[name] = this.prototype[name]; 
return; 
} 
else 
{ 
//没有对父类构造函数的调用时, 自动添加 
b = this.FSuperClass.FClassName + ".apply(this,arguments);" + s; 
} 
//编译结果赋值 
if (name) 
{ 
eval(cn + ".prototype." + name + "=function(" + arg + "){" + b + "}"); 
} 
else 
{ 
eval(cn + "=function(" + arg + "){" + b + ";if(this.constructor==" + cn + ")" + cn + "._" + cn + ".apply(this,arguments);}"); 
window[cn].FClassName = cn; 
} 
} 
Function.prototype.extendsOf = function(superClass) 
{ 
this.FSuperClass = superClass; 
//编译类的全部函数 
this.FCompile(); 
for (var name in this.prototype) 
{ 
this.FCompile(name); 
} 
var clazz = window[this.FClassName]; 
clazz.FSuperClass = superClass; 
//复制父类中子类没有实现的函数和属性 
var prototype = clazz.prototype; 
for (var name in superClass.prototype) 
{ 
if (!prototype[name]) 
{ 
prototype[name] = superClass.prototype[name]; 
} 
} 
//复制初始化方法, 形式如Object._Object 
for (var c = this; ; c = c.FSuperClass) 
{ 
if (c["_" + c.FClassName]) 
{ 
clazz["_" + clazz.FClassName] = c["_" + c.FClassName]; 
return; 
} 
} 
} 
/* 
内置Object类为OOP提供的支持 
*/ 
Object.FClassName = "Object"; 
Object._Object = Function.Instance; 
Object.prototype.instanceOf = function(clazz) 
{ 
for (var c = this.constructor; c; c = c.FSuperClass) 
{ 
if (c === clazz) 
{ 
return true; 
} 
} 
return false; 
}
Javascript 相关文章推荐
js 利用className得到对象的实现代码
Nov 15 Javascript
ExtJS的拖拽效果示例
Dec 09 Javascript
jQuery中has()方法用法实例
Jan 06 Javascript
jquery中one()方法的用法实例
Jan 16 Javascript
Jquery使用val方法读写value值
May 18 Javascript
如何理解Vue的render函数的具体用法
Aug 30 Javascript
分析javascript原型及原型链
Mar 18 Javascript
Vuex新手的理解与使用详解
May 31 Javascript
微信域名检测接口调用演示步骤(含PHP、Python)
Dec 08 Javascript
在vue中使用echarts(折线图的demo,markline用法)
Jul 20 Javascript
Vue循环中多个input绑定指定v-model实例
Aug 31 Javascript
JS不要再到处使用绝对等于运算符了
Apr 30 Javascript
javascript 鼠标滚轮事件
Apr 09 #Javascript
File文件控件,选中文件(图片,flash,视频)即立即预览显示
Apr 09 #Javascript
用js实现的检测浏览器和系统的函数
Apr 09 #Javascript
常用简易JavaScript函数
Apr 09 #Javascript
javascript fullscreen全屏实现代码
Apr 09 #Javascript
jQuery 插件 将this下的div轮番显示
Apr 09 #Javascript
javascript RadioButtonList获取选中值
Apr 09 #Javascript
You might like
php邮件发送,php发送邮件的类
2011/03/24 PHP
php获取手机端的号码以及ip地址实例代码
2018/09/12 PHP
Jquery之美中不足小结
2011/02/16 Javascript
JavaScript对象和字串之间的转换实例探讨
2013/04/21 Javascript
JavaScript中的console.trace()函数介绍
2014/12/29 Javascript
JavaScript实现向setTimeout执行代码传递参数的方法
2015/04/16 Javascript
jquery插件Jplayer使用方法简析
2016/04/22 Javascript
好好了解一下Cookie(强烈推荐)
2016/06/14 Javascript
ajax 提交数据到后台jsp页面及页面跳转问题
2017/01/19 Javascript
vue.js树形组件之删除双击增加分支实例代码
2017/02/28 Javascript
微信小程序实现指定显示行数多余文字去掉用省略号代替
2018/07/25 Javascript
vue动态改变背景图片demo分享
2018/09/13 Javascript
cocos2dx+lua实现橡皮擦功能
2018/12/20 Javascript
angular 实现同步验证器跨字段验证的方法
2019/04/11 Javascript
vue如何限制只能输入正负数及小数
2019/07/04 Javascript
详解vue3.0 的 Composition API 的一种使用方法
2020/10/26 Javascript
解决vue侦听器watch,调用this时出现undefined的问题
2020/10/30 Javascript
跟老齐学Python之有点简约的元组
2014/09/24 Python
Python实现获取命令行输出结果的方法
2017/06/10 Python
python微信跳一跳系列之棋子定位像素遍历
2018/02/26 Python
解决PyCharm import torch包失败的问题
2018/10/13 Python
Python构建图像分类识别器的方法
2019/01/12 Python
Python学习笔记基本数据结构之序列类型list tuple range用法分析
2019/06/08 Python
如何解决django-celery启动后迅速关闭
2019/10/16 Python
使用Python完成15位18位身份证的互转功能
2019/11/06 Python
解决IDEA 的 plugins 搜不到任何的插件问题
2020/05/04 Python
PyQt5.6+pycharm配置以及pyinstaller生成exe(小白教程)
2020/06/02 Python
Python调用shell cmd方法代码示例解析
2020/06/18 Python
CSS3悬停效果案例应用
2012/11/21 HTML / CSS
发现世界上最好的珠宝设计师:JewelStreet
2017/12/17 全球购物
印度手工编织服装和家居用品商店:Fabindi
2019/10/07 全球购物
Trench London官方网站:高级风衣和意大利皮夹克
2020/07/11 全球购物
求两个数的乘积和商数,该作用由宏定义来实现
2013/03/13 面试题
党员反对四风思想汇报范文
2014/10/25 职场文书
关于元旦的广播稿2016
2015/12/17 职场文书
Python自动化实战之接口请求的实现
2022/05/30 Python