现在的位置: 首页 > 综合 > 正文

[讨论]javascript设计模式交流(二) ——Prototype Pattern

2013年05月24日 ⁄ 综合 ⁄ 共 6358字 ⁄ 字号 评论关闭
导读:
  javascript设计模式交流(二) ——Prototype Pattern
  本文将讨论Prototype Pattern的js实现,之后讨论javascript的prototype继承和Prototype Pattern的关系。
  Prototype Pattern是一种创建型模式,在GoF Book中它的意图被描述成用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
  Prototype Pattern本身实际上非常简单,任何一个提供了clone()方法的对象都可以成为原型对象,所有通过它复制的对象都属于一类对象。在静态语言中,这一模式被用于运行时指定对象类型,相比工厂模式,这一模式无需创建与类层次平行的工厂类结构,实现上要方便得多。
  clone的三种js实现
  在js中,clone方法的实现并不困难,对js的6种基本类型来说 string boolean undefined null number皆可直接用=赋值,唯一麻烦的是object。
  对object 我们可以用clone其所有成员的方式复制 作为方法的函数可以这样定义:
  [复制] Code:
  /************************************/
  function objectClone()
  {
  var ret=new Object();
  for(var p in this)
  {
  ret[p]=this[p];
  }
  return ret }
  但我们显然面对着一个问题:this[p]也可能是一个object 所以很可能我们需要用递归来实现deepClone
  [复制] Code:
  /************************************/
  function objectDeepClone()
  {
  var ret=new Object();
  for(var p in this)
  {
  if(typeof ret[p]!=object)ret[p]=this[p];
  else ret[p]=objectDeepClone.call(this[p]);
  }
  return ret }/************************************/
  对js来说 实现clone还有另外一种方式,在javascript中,构造器的prototype属性指明了某一类的原型,当实例化时,这一原型被作为对象的原型使用。特别地,这个prototype对象也可能是从某一原型构造出来的,这形成了一个类似继承的结构,所以javascript的面向原型特性又被称作原型继承(尽管我很不赞同这种做法,还是要提一下)。
  回到我们前面的Prototype Pattern,javascript天生的引用型原型继承为我们提供了另外一种clone的实现方式:
  [复制] Code:
  /************************************/
  function objectPrototypeClone()
  {
  var tmp=function(){};
  tmp.prototype=this return new tmp }/************************************/
  这样clone出来的对象只读地共享一个原型的属性,它的最大优势是速度非常快,当我们希望快速复制大型对象时,可以使用这种方式,但是它会造成访问速度降低,而且它实时反映父节点的变化。
  内置对象的clone
  但是,到这里为止,我们还没有考虑内置对象,内置对象不能用普通方法clone 我们要考虑的内置对象有这么几个:
  Function Array String Boolean Number Date
  RegExp Error和Math没有需要clone的场景 所以不在我们的考虑之中。
  对Function来说,完全产生一个副本是不可能的,因为我们无法保证构造的函数跟原来的函数在同一作用域,但是不包含作用域的实现是很容易的:
  eval(this);
  或者使用Function构造
  return Function(new String("return ")+this)();
  Function本身是个Object 因此必须加上Object的clone 实现functionPrototypeClone需要一点小花招
  [复制] Code:
  /************************************/
  function functionClone()
  {
  var ret=Function(new String("return ")+this)();
  for(var p in this)
  {
  ret[p]=this[p];
  }
  return ret }/************************************/
  function functionDeepClone()
  {
  var ret=Function(new String("return ")+this)()
  for(var p in this)
  {
  if(typeof ret[p]!=object)ret[p]=this[p];
  ret[p]=objectDeepClone.call(this[p]);
  }
  return ret }/************************************/
  function functionPrototypeClone()
  {
  var tmp=Function.prototype Function.prototype=this var ret=(new Function(new String("return ")+this))();
  Function.prototype=tmp return ret }/************************************/
  Array只要保证length正确就可以了
  [复制] Code:
  /************************************/
  function arrayClone()
  {
  var ret=new Array();
  for(var p in this)
  {
  ret[p]=this[p];
  }
  return ret }/************************************/
  function arrayDeepClone()
  {
  var ret=new Array();
  for(var p in this)
  {
  if(typeof ret[p]!=object)ret[p]=this[p];
  ret[p]=objectDeepClone.call(this[p]);
  }
  return ret }/************************************/
  function arrayPrototypeClone()
  {
  var tmp=Array.prototype Array.prototype=this var ret=new Array();
  Array.prototype=tmp return ret }/************************************/
  Date对象提供了getTime 所以可以很容易实现
  [复制] Code:
  /************************************/
  function dateClone()
  {
  var ret=new Date();
  ret.setTime(this.getTime());
  for(var p in this)
  {
  ret[p]=this[p];
  }
  return ret }/************************************/
  function dateDeepClone()
  {
  var ret=new Date();
  ret.setTime(this.getTime());
  for(var p in this)
  {
  if(typeof ret[p]!=object)ret[p]=this[p];
  ret[p]=objectDeepClone.call(this[p]);
  }
  return ret }/************************************/
  function datePrototypeClone()
  {
  var tmp=Date.prototype Date.prototype=this var ret=new Date();
  ret.setTime(this.getTime());
  Date.prototype=tmp return ret }/************************************/
  String Boolean Number都是只读的对象,所以只要=就可以了。
  完美的clone
  为了实现完整的clone三种类型,要把上面的方法结合起来。
  [复制] Code:
  /************************************/
  function clone()
  {
  var ret if(this instanceof Function)
  {
  ret=Function(new String("return ")+this)();
  }
  else if(this instanceof Array)
  {
  ret=new Array();
  }
  else if(this instanceof Date)
  {
  ret=new Date();
  ret.setTime(this.getTime());
  }
  else if( (this instanceof String) || (this instanceof Boolean) || (this instanceof Number) )
  {
  return this }
  else ret=new Object();
  for(var p in this)
  {
  ret[p]=this[p];
  }
  return ret }
  function deepClone()
  {
  var ret if(this instanceof Function)
  {
  ret=Function(new String("return ")+this)();
  }
  else if(this instanceof Array)
  {
  ret=new Array();
  }
  else if(this instanceof Date)
  {
  ret=new Date();
  ret.setTime(this.getTime());
  }
  else if( (this instanceof String) || (this instanceof Boolean) || (this instanceof Number) )
  {
  return this }
  else ret=new Object();
  for(var p in this)
  {
  if(typeof ret[p]!="object")ret[p]=this[p];
  else ret[p]=deepClone.call(this[p]);
  }
  return ret }
  function prototypeClone()
  {
  if(this instanceof Function)
  {
  var tmp=Function.prototype Function.prototype=this var ret=(new Function(new String("return ")+this))();
  Function.prototype=tmp return ret }
  else if(this instanceof Array)
  {
  var tmp=Array.prototype Array.prototype=this var ret=new Array();
  Array.prototype=tmp return ret }
  else if(this instanceof Date)
  {
  var tmp=Date.prototype Date.prototype=this var ret=new Date();
  ret.setTime(this.getTime());
  Date.prototype=tmp return ret }
  else if( (this instanceof String) || (this instanceof Boolean) || (this instanceof Number) )
  {
  return this }
  else
  {
  var constructor=function(){};
  constructor.prototype=this return new constructor }
  }
  前面讨论了三种Clone的实现方法,它们各自具有适合的语义环境,比如对一个数组来说 若是把它理解为一个集合Collection 则应该使用浅clone(假如集合A是B的子集,则应保证A.clone()亦是B的子集),若是把它理解为一个向量Vector,则应使用深clone(保证对向量A的分量操作不应影响向量A.clone()的分量)。prototypeClone的一个最常见的应用场景是深度优先搜索算法算法,为了扩展解空间树,我们通常需要快速的构造一个副本,如果使用clone或者deepClone 这将非常慢,而深度优先搜索的特点是在字节点被销毁之前,父节点不会变化,所以prototypeClone是非常合适的。
  附:Prototype-oriented Programming和Prototype Pattern
  面向原型的语言思想跟原型模式是完全一致的:从同一原型clone出来的对象就是一类对象。Prototype-oriented的语言对这种模式提供了语言级别的支持,即所有"类"的定义都是通过指定该类的一个原型来实现的(Class-Based Programming是通过类结构声明来描述一类对象,meta-class则是通过构造一个"类对象"来描述一类对象)。每次实例话就clone一次原型,然而这种方式会造成信息的冗余:所有对象都持有原型对象的一个clone的副本,而且一旦某一对象被构造,修改原型不会对它造成任何影响,这对于希望在程序中统一改变某一类对象的人来说很不方便。于是,一种变通的方法产生了:引用型原型对象,与之相对,原来的原型对象使用方法被称为 复制型原型对象。引用型原型对象不再clone原型,而是保存一个指向原型的指针,当访问属性时,首先检查自己的属性,当查到不存在时,则通过指针向原型索取相应属性。而引用型原型就是javascript的面向原型特性的实现方式。
  PS.本文征求过月老大意见了 不是盗版滴 呵呵
  后面补上UML图 gif版和visio版
  [本帖最后由 winter 于 2007-11-26 11:48 编辑 ]
  
  
  附件: 您所在的用户组无法下载或查看附件

本文转自
http://bbs.51js.com/viewthread.php?tid=74167&highlight=%2B%C9%E8%BC%C6%C4%A3%CA%BD%2B

抱歉!评论已关闭.