javascript设计模式交流(二) ——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 clone()
        {
                var 
ret=new Object();
                for(var 
p in this)
                {
                        
ret[p]=this[p];
                }
        } 



但我们显然面对着一个问题:this[p]也可能是一个object 所以很可能我们需要用递归来实现deepClone

Code:

        function deepClone()
        {
                var 
ret=new Object();
                for(var 
p in this)
                {
                        
ret[p]=deepClone.call(this[p]);
                }
        } 



对js来说 实现clone还有另外一种方式,在javascript中,构造器的prototype属性指明了某一类的原型,当实例化时,这一原型被作为对象的原型使用。特别地,这个prototype对象也可能是从某一原型构造出来的,这形成了一个类似继承的结构,所以javascript的面向原型特性又被称作原型继承(尽管我很不赞同这种做法,还是要提一下)。

    回到我们前面的Prototype Pattern,javascript天生的引用型原型继承为我们提供了另外一种clone的实现方式:

Code:

        function prototypeClone()
        {
                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];
                }
        }
        function 
functionDeepClone()
        {
                var 
ret=Function(new String("return ")+this)()
                for(var 
p in this)
                {
                        
ret[p]=deepClone.call(this[p]);
                }
        }
        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];
                }
        }
        function 
arrayDeepClone()
        {
                var 
ret=new Array();
                for(var 
p in this)
                {
                        
ret[p]=deepClone.call(this[p]);
                }
        }
        function 
arrayPrototypeClone()
        {                
                var 
tmp=Array.prototype;
                Array.
prototype=this;
                var 
ret=new Array();
                Array.
prototype=tmp;
                return 
ret;
        } 



Date对象提供了getTime 所以可以很容易实现

Code:

        function arrayClone()
        {
                var 
ret=new Date();
                
ret.setTime(this.getTime());
                for(var 
p in this)
                {
                        
ret[p]=this[p];
                }
        }
        function 
arrayDeepClone()
        {
                var 
ret=new Date();
                
ret.setTime(this.getTime());

                for(var 
p in this)
                {
                        
ret[p]=deepClone.call(this[p]);
                }
        }
        function 
arrayPrototypeClone()
        {                
                var 
tmp=Date.prototype;
                
Date.prototype=this;
                var 
ret=new Date();
                
ret.setTime(this.getTime());
                
Date.prototype=tmp;
                return 
ret;
        } 



String Boolean Number都是只读的对象,所以只要=就可以了。



    前面讨论了三种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.本文征求过本系列原作者意见了 不是盗版滴 呵呵
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript 设计模式是在 JavaScript 编程中经常使用的一种代码组织和架构方法。设计模式可以帮助开发者解决常见的问题,并提供可复用的解决方案。 以下是一些常见的 JavaScript 设计模式: 1. 工厂模式(Factory Pattern):通过使用工厂方法创建对象,将对象的创建和使用分离开来,提高代码的可扩展性和可维护性。 2. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点来访问该实例。 3. 观察者模式(Observer Pattern):定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会被自动通知并更新。 4. 发布-订阅模式(Publish-Subscribe Pattern):也是一种观察者模式的变体,在这种模式中,发布者(Publisher)和订阅者(Subscriber)之间通过消息队列进行通信。 5. 原型模式(Prototype Pattern):通过复制现有对象来创建新对象,避免了使用类进行实例化的复杂性。 6. 适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口,使得原本不兼容的类可以一起工作。 7. 装饰者模式(Decorator Pattern):动态地给对象添加新的功能,而不影响其他对象。 8. 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以互换使用。 这些设计模式可以帮助开发者在编写 JavaScript 代码时更好地组织和设计代码结构,提高代码的可读性、可维护性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值