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

复制代码 代码如下:

复制代码 代码如下:

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;
}

时间: 2024-09-20 15:43:30

javascript 原型模式实现OOP的再研究_javascript技巧的相关文章

JavaScript严格模式禁用With语句的原因_javascript技巧

看了很多遍JavaScript严格模式,其中有说"禁用With语句",以前看到这都是骑马观花,一带而过,因为平时就很少用到这个语句,禁不禁用对自己关系都不是很大.今天禁不住想知道为何"严格模式"就容不下with语句呢? EcmaScript规范上说"with 语句用于设置代码在特定对象中的作用域",可以看出,With语句改变了作用域链. function Person(name,age,sex){ this.name = name; this.a

javascript 原型链维护和继承详解_javascript技巧

一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了. 其实啊,光靠这一个属性是无法完成javascript的继承. 我们在代码中使用的prototype完成继承在这里就不多说了.大家可以查一下资料. 另外一个看不见的prototype成员. 每一个实例都有有一条指向原型的prototype属性,这个属性是无法被访问到的,当然也就无法被修改了,因为这是维护javascript继承的基础. 复

JavaScript中的Web worker多线程API研究_javascript技巧

HTML5支持了Web Worker这样的API,允许网页在安全的情况下执行多线程代码.不过Web Worker实际上受到很多限制,因为它无法真正意义上共享内存数据,只能通过消息来做状态通知,所以甚至不能称之为真正意义上的"多线程". Web Worker的接口使用起来很不方便,它基本上自带一个sandbox,在沙箱中跑一个独立的js文件,通过 postMessage和 onMessge来和主线程通信: 复制代码 代码如下: var worker = new Worker("

javascript原型模式用法实例详解

  本文实例讲述了javascript原型模式用法.分享给大家供大家参考.具体分析如下: 一般在了解了工厂模式和构造函数模式的弊端之后,就知道为什么需要原型模式了 原型模式i的定义:每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法.比如在构造函数模型中sayInformation()方法,如果声明两个实例就要构造两次sayInformation方法,但是声明两次是没有必要的,这就是为什么有原型模式的出现(尼玛,网上那些博客

Javascript原型模式总结梳理

在大多数面向对象语言中,对象总是由类中实例化而来,类和对象的关系就像模具跟模件一样.Javascript中没有类的概念,就算ES6中引入的class也不过是一种语法糖,本质上还是利用原型实现.在原型编程语言中,类并不是必需的,对象不一定需要由类实例化而来,而是通过克隆另外一个对象来得到. 原型模式是用来创建对象的一种模式.在以类为中心的语言中,要创建一个对象首先要指定这个对象的类型,然后实例化一个对象.使用原型模式创建对象时不必关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样

javascript原型模式用法实例详解_javascript技巧

本文实例讲述了javascript原型模式用法.分享给大家供大家参考.具体分析如下: 一般在了解了工厂模式和构造函数模式的弊端之后,就知道为什么需要原型模式了   原型模式i的定义:每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法.比如在构造函数模型中sayInformation()方法,如果声明两个实例就要构造两次sayInformation方法,但是声明两次是没有必要的,这就是为什么有原型模式的出现(尼玛,网上那些博客

详述JavaScript实现继承的几种方式(推荐)_javascript技巧

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的. 原型链 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针.如果:我们让原型对象A等于另一个类型B的实例,那么原型对象A就会有一个指针指向B的原型对象,相应的B的原型对象中保存着指向其构造函数的指针.假如B的原型对象又是另一个类型的实例,那么上述的关系依旧成立,如此层层递进,就构成了实例与原型的

JS原型链 详解及示例代码_javascript技巧

前言 在 segmentfault 上看到这样一道题目: var F = function(){}; Object.prototype.a = function(){}; Function.prototype.b = function(){}; var f = new F(); 问:f 能取到a,b吗?原理是什么? 乍一看真的有点懵,仔细研究了一下,发现还是对原型理解不透彻,所以总结一篇,填个洞~ Function和Object 在解题之前,先再说说 原型.原型链,以及 Function 和 O

JavaScript定义类的几种方式总结_javascript技巧

提起面向对象我们就能想到类,对象,封装,继承,多态.在<javaScript高级程序设计>(人民邮电出版社,曹力.张欣译.英文名字是:Professional JavaScript for Web Developers)这本书中描述的还算比较详细.我们看看JavaScript中定义类的各种方法. 1.工厂方式 javaScript中创建自己的类和对象,我们应该是必须掌握的,我们都知道javaScript中对象的属性可以在对象创建后动态定义,比如下面的代码: 复制代码 代码如下: <scr