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 相关文章推荐
IE中createElement需要注意的一个问题
Jul 13 Javascript
JS动态添加Table的TR,TD实现方法
Jan 28 Javascript
AngularJS的内置过滤器详解
May 14 Javascript
JavaScript处理解析JSON数据过程详解
Sep 11 Javascript
基于BootStrap的图片轮播效果展示实例代码
May 23 Javascript
jQuery实现的模拟弹出窗口功能示例
Nov 24 Javascript
jQuery常用选择器详解
Jul 17 jQuery
解决vue router使用 history 模式刷新后404问题
Jul 19 Javascript
node中间层实现文件上传功能
Jun 11 Javascript
使用NestJS开发Node.js应用的方法
Dec 03 Javascript
node.js express框架实现文件上传与下载功能实例详解
Oct 15 Javascript
vue3.0实现点击切换验证码(组件)及校验
Nov 18 Vue.js
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
hessian 在PHP中的使用介绍
2010/12/13 PHP
flash用php连接数据库的代码
2011/04/21 PHP
PHP递归遍历指定目录的文件并统计文件数量的方法
2015/03/24 PHP
深入浅析PHP7.0新特征(五大新特征)
2015/10/29 PHP
PHP+JS三级菜单联动菜单实现方法
2016/02/24 PHP
php 变量引用与变量销毁机制详细介绍
2016/12/05 PHP
php实现多站点共用session实现单点登录的方法详解
2019/09/18 PHP
帮助避免错误的Javascript陷阱清单
2009/05/31 Javascript
JavaScript高级程序设计 错误处理与调试学习笔记
2011/09/10 Javascript
web性能优化之javascript性能调优
2012/12/28 Javascript
JS Date函数整理方便使用
2013/10/23 Javascript
解决jquery版本冲突的有效方法
2014/09/02 Javascript
详谈nodejs异步编程
2014/12/04 NodeJs
实践中学习AngularJS表单
2016/03/21 Javascript
JS获取html元素的标记名实现方法
2016/10/08 Javascript
代码详解javascript模块加载器
2018/03/04 Javascript
详解JavaScript的变量
2019/04/04 Javascript
vue 开发企业微信整合案例分析
2019/12/02 Javascript
[02:48]DOTA2超级联赛专访海涛:你们的选择没有错
2013/06/07 DOTA
Python ORM框架SQLAlchemy学习笔记之关系映射实例
2014/06/10 Python
Python中使用pprint函数进行格式化输出的教程
2015/04/07 Python
Centos部署django服务nginx+uwsgi的方法
2019/01/02 Python
Django保护敏感信息的方法示例
2019/05/09 Python
Python 等分切分数据及规则命名的实例代码
2019/08/16 Python
pytorch torchvision.ImageFolder的用法介绍
2020/02/20 Python
Python并发请求下限制QPS(每秒查询率)的实现代码
2020/06/05 Python
python中有帮助函数吗
2020/06/19 Python
判断单链表中是否存在环
2012/07/16 面试题
安全责任书范文
2014/08/25 职场文书
2014最新离职证明范本
2014/09/12 职场文书
教你用Java在个人电脑上实现微信扫码支付
2021/06/13 Java/Android
mysql连接查询中and与where的区别浅析
2021/07/01 MySQL
sass 常用备忘案例详解
2021/09/15 HTML / CSS
【海涛教你打DOTA】黑鸟第一视角解说
2022/04/01 DOTA
win10忘记pin密码登录不了怎么办?win10忘记pin密码登不进去的解决方法
2022/07/07 数码科技
详解SQL报错盲注
2022/07/23 SQL Server