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 open() 与showModalDialog()方法使用介绍
Sep 10 Javascript
Express作者TJ告别Node.js奔向Go
Jul 14 Javascript
jquery带翻页动画的电子杂志代码分享
Aug 21 Javascript
快速学习jQuery插件 Form表单插件使用方法
Dec 01 Javascript
JS组件Bootstrap Select2使用方法详解
Apr 17 Javascript
jQuery中$.each()函数的用法引申实例
May 12 Javascript
Vue2.0 UI框架ElementUI使用方法详解
Apr 14 Javascript
javascript定时器取消定时器及优化方法
Jul 08 Javascript
JQuery元素快速查找与操作
Apr 22 jQuery
浅谈vux之x-input使用以及源码解读
Nov 04 Javascript
基于layPage插件实现两种分页方式浅析
Jul 27 Javascript
vue 解决setTimeOut和setInterval函数无效报错的问题
Jul 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分页集合包括使用方法
2013/10/21 PHP
3种方法轻松处理php开发中emoji表情的问题
2016/07/18 PHP
关于setInterval、setTimeout在jQuery中的使用注意事项
2011/09/28 Javascript
Javascript实现可旋转的圆圈实例代码
2015/08/04 Javascript
JavaScript的面向对象编程基础
2015/08/13 Javascript
详解vue的数据binding绑定原理
2017/04/12 Javascript
微信小程序 http请求的session管理
2017/06/07 Javascript
微信小程序组件 marquee实例详解
2017/06/23 Javascript
JavaScript原型继承_动力节点Java学院整理
2017/06/30 Javascript
使用Vuex实现一个笔记应用的方法
2018/03/13 Javascript
在Python中使用sort()方法进行排序的简单教程
2015/05/21 Python
Python基本语法经典教程
2016/03/11 Python
Python-while 计算100以内奇数和的方法
2019/06/11 Python
PIL图像处理模块paste方法简单使用详解
2019/07/17 Python
python使用百度文字识别功能方法详解
2019/07/23 Python
python对XML文件的操作实现代码
2020/03/27 Python
Django实现whoosh搜索引擎使用jieba分词
2020/04/08 Python
解决python多线程报错:AttributeError: Can't pickle local object问题
2020/04/08 Python
Python垃圾回收机制三种实现方法
2020/04/27 Python
Python数据相关系数矩阵和热力图轻松实现教程
2020/06/16 Python
tensorflow图像裁剪进行数据增强操作
2020/06/30 Python
Python如何读写二进制数组数据
2020/08/01 Python
python 爬取免费简历模板网站的示例
2020/09/27 Python
使用css3 属性如何丰富图片样式(圆角 阴影 渐变)
2012/11/22 HTML / CSS
html5适合移动应用开发的12大特性
2014/03/19 HTML / CSS
悦木之源美国官网:Origins美国
2016/08/01 全球购物
香港演唱会订票网站:StubHub香港
2019/10/10 全球购物
你们项目是如何进行变更控制的
2015/08/26 面试题
七年级生物教学反思
2014/01/30 职场文书
保密承诺书范文
2014/03/27 职场文书
《泉水》教学反思
2014/04/11 职场文书
我爱我家教学反思
2014/05/01 职场文书
弘扬焦裕禄精神走群众路线思想汇报
2014/09/12 职场文书
新年晚会开场白
2015/05/29 职场文书
《秋天的怀念》教学反思
2016/02/17 职场文书
面试被问select......for update会锁表还是锁行
2021/11/11 MySQL