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 相关文章推荐
jquery插件制作 手风琴Panel效果实现
Aug 17 Javascript
js传中文参数controller里获取参数乱码问题解决方法
Jan 03 Javascript
javascript中alert()与console.log()的区别
Aug 26 Javascript
JavaScript判断表单为空及获取焦点的方法
Feb 12 Javascript
javascript鼠标滑过显示二级菜单特效
Nov 18 Javascript
JS操作input标签属性checkbox全选的实现代码
Mar 02 Javascript
BootStrap 表单控件之单选按钮水平排列
May 23 Javascript
JS库之Waypoints的用法详解
Sep 13 Javascript
Vue组件中prop属性使用说明实例代码详解
May 31 Javascript
JavaScript偏函数与柯里化实例详解
Mar 27 Javascript
详解VSCode配置启动Vue项目
May 14 Javascript
Vue-cli3.X使用px2 rem遇到的问题及解决方法
Aug 08 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及Zend Engine的线程安全模型分析
2011/11/10 PHP
基于curl数据采集之单页面采集函数get_html的使用
2013/04/28 PHP
thinkphp5 migrate数据库迁移工具
2018/02/20 PHP
JavaScript中合并数组的N种方法
2014/09/16 Javascript
javascript实现图片延迟加载方法汇总(三种方法)
2015/08/27 Javascript
基于jQuery和Bootstrap框架实现仿知乎前端动态列表效果
2016/11/09 Javascript
Angular4绑定html内容出现警告的处理方法
2017/11/03 Javascript
JS改变页面颜色源码分享
2018/02/24 Javascript
vue项目关闭eslint校验
2018/03/21 Javascript
JS中数组与对象的遍历方法实例小结
2018/08/14 Javascript
layui radio性别单选框赋值方法
2018/08/15 Javascript
js实现倒计时器自定义时间和暂停
2019/02/25 Javascript
微信小程序自定义toast组件的方法详解【含动画】
2019/05/11 Javascript
深入理解基于vue-cli的webpack打包优化实践及探索
2019/10/14 Javascript
浅谈layui数据表格判断问题(加入表单元素),设置单元格样式
2019/10/26 Javascript
使用vant的地域控件追加全部选项
2020/11/03 Javascript
Python自然语言处理之词干,词形与最大匹配算法代码详解
2017/11/16 Python
python数字图像处理实现直方图与均衡化
2018/05/04 Python
PyQt5 QListWidget选择多项并返回的实例
2019/06/17 Python
将Pytorch模型从CPU转换成GPU的实现方法
2019/08/19 Python
Python closure闭包解释及其注意点详解
2019/08/28 Python
python 3.6.7实现端口扫描器
2019/09/04 Python
浅谈python已知元素,获取元素索引(numpy,pandas)
2019/11/26 Python
Python 3 使用Pillow生成漂亮的分形树图片
2019/12/24 Python
django的403/404/500错误自定义页面的配置方式
2020/05/21 Python
Giglio德国网上精品店:奢侈品服装和配件
2016/09/23 全球购物
加拿大折扣、优惠券和交易网站:WagJag
2018/02/07 全球购物
中国一家专注拼团的社交购物网站:拼多多
2018/06/13 全球购物
SteelSeries赛睿官网:游戏外设和配件的领先制造商(耳机、键盘、鼠标和鼠标垫)
2018/06/17 全球购物
介绍JAVA 中的Collection FrameWork(及如何写自己的数据结构)
2014/10/31 面试题
Ruby如何进行文件操作
2014/07/17 面试题
助人为乐模范事迹材料
2014/06/02 职场文书
王金山在党的群众路线教育实践活动总结大会上的讲话稿
2014/10/25 职场文书
幼儿园感谢信
2015/01/21 职场文书
工程催款通知书
2015/04/17 职场文书
Python内置类型集合set和frozenset的使用详解
2022/04/26 Python