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 相关文章推荐
仅Firefox中链接A无法实现模拟点击以触发其默认行为
Jul 31 Javascript
无刷新预览所选择的图片示例代码
Apr 02 Javascript
JavaScript闭包实例讲解
Apr 22 Javascript
JavaScript字符串常用类使用方法汇总
Apr 14 Javascript
JavaScript字符串对象(string)基本用法示例
Jan 18 Javascript
angular或者js怎么确定选中ul中的哪几个li
Aug 16 Javascript
vue.js语法及常用指令
Oct 29 Javascript
JavaScript实现单例模式实例分享
Dec 22 Javascript
jQuery实现表单动态添加与删除数据操作示例
Jul 03 jQuery
vue forEach循环数组拿到自己想要的数据方法
Sep 21 Javascript
JavaScript禁用右键单击优缺点分析
Jan 20 Javascript
JS实现简单省市二级联动
Nov 27 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的日期处理函数及uchome的function_coomon中日期处理函数的研究
2011/01/12 PHP
PHP中比较时间大小实例
2014/08/21 PHP
PHP中实现crontab代码分享
2015/03/26 PHP
PHP实现从PostgreSQL数据库检索数据分页显示及根据条件查找数据示例
2018/06/09 PHP
PHP中__set()实例用法和基础讲解
2019/07/23 PHP
解决laravel id非自增 模型取回为0 的问题
2019/10/11 PHP
js文件中调用js的实现方法小结
2009/10/23 Javascript
JavaScript中数组的合并以及排序实现示例
2015/10/24 Javascript
EasyUI学习之Combobox下拉列表(1)
2016/12/29 Javascript
jQuery Masonry瀑布流插件使用方法详解
2017/01/18 Javascript
JS实现AES加密并与PHP互通的方法分析
2017/04/19 Javascript
详解Javascript 中的 class、构造函数、工厂函数
2017/12/20 Javascript
Javascript实现购物车功能的详细代码
2018/05/08 Javascript
angularjs 动态从后台获取下拉框的值方法
2018/08/13 Javascript
浅谈Angular7 项目开发总结
2018/12/19 Javascript
js实现通过开始结束控制的计时器
2019/02/25 Javascript
[13:16]INFAMOUS vs VGJ T BO3
2018/06/07 DOTA
在Docker上部署Python的Flask框架的教程
2015/04/08 Python
Python 常用的安装Module方式汇总
2017/05/06 Python
Python判断一个文件夹内哪些文件是图片的实例
2018/12/07 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
2019/08/05 Python
Django设置Postgresql的操作
2020/05/14 Python
python中加背景音乐如何操作
2020/07/19 Python
jupyter 添加不同内核的操作
2021/02/06 Python
HTML5 和小程序实现拍照图片旋转、压缩和上传功能
2018/10/08 HTML / CSS
HTML5 Canvas玩转酷炫大波浪进度图效果实例(附demo)
2016/12/14 HTML / CSS
兰蔻加拿大官方网站:Lancome加拿大
2016/08/05 全球购物
会计专业毕业生自我评价
2013/09/25 职场文书
工地门卫岗位职责
2013/12/30 职场文书
资源工程专业毕业生求职信
2014/02/27 职场文书
大学生党校培训心得体会
2014/09/11 职场文书
2015年世界无烟日活动方案
2015/05/04 职场文书
2015年音乐教学工作总结
2015/07/22 职场文书
德劲DE1108畅想
2021/04/22 无线电
Android自定义双向滑动控件
2022/04/19 Java/Android
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
2022/05/25 SQL Server