装饰者模式

可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰者模式。

代码例子

     var Plane = function(){}
Plane.prototype.fire = function(){
console.log( '发射普通子弹' );
}
var one = function( plane ){ //装饰类 one
this.plane = plane;
}
one.prototype.fire = function(){
this.plane.fire();
console.log( '发射导弹' );
}
var two = function( plane ){ //装饰类 two
this.plane = plane;
}
two.prototype.fire = function(){
this.plane.fire();
console.log( '发射原子弹' );
} var plane = new Plane();
plane = new one( plane ); //装饰对象会覆盖原来的对象,这么做是因为装饰者模式是为了添加职能,可能这时候的代码量已经很大了,原对象的方法可能在很多地方都调用过,所以我们通过这种方式,追加了功能而不用修改太多代码。
plane = new two( plane ); /**装饰对象调用**/
plane.fire(); /* 发射普通子弹
发射导弹
发射原子弹
*/

从代码我们可以看出,装饰者模式这种给对象动态增加职责的方式,并没有真正地改动对象自身。

从形式上来看,装饰者模式就好像给原来的对象包裹了一层,产生一个新的对象,同时,保留了接口统一。装饰类的作用就是接受一个对象,然后把它变成功能更多的对象。

装饰类接收原来的对象,会把它保存下来,调用装饰类时,会将本来的对象放入另一个对象之中,这些对象以一条链的方式进行引用,形成一个聚合对象。这些对象都拥有相同的接口(fire方法),当请求达到链中的某个对象时,这个对象会执行自身的操作,随后把请求转发给链中的下一个对象。

从传递请求这一点来看有点类似于职责链,但是职责链是传递请求给正确执行的对象,而这个不但自身会执行,通过装饰类添加的方法也会执行,它这种类似于链子一样的请求调用与其说是链子倒不如说是包裹更合适。装饰类会把原来的对象包裹起来,调用时会一层一层的调用。

更js的实现方法

因为js是动态语言,修改类型,改变对象的属性和方法对它来说太简单了,所以还有一种思路,我们拿到需要调用的方法的引用,然后直接对它进行改造即可。不过这种方式等于直接修改原对象的方法,和上面的方法哪个更好,我说不上来。

     var plane = {      //原对象
fire: function(){
console.log( '发射普通子弹' );
}
}
var add_one = function(){ //想要添加的功能
console.log( '发射导弹' );
}
var add_two = function(){
console.log( '发射原子弹' );
} var fire1 = plane.fire; //存储对象方法的引用
plane.fire = function(){ //改写对象方法
fire1(); //原对象的方法
add_one(); //添加的方法
} var fire2 = plane.fire; //同上
plane.fire = function(){
fire2();
add_two();
} plane.fire(); /* 发射普通子弹
发射导弹
发射原子弹
*/

另外我还有一个不成熟的想法,像第一种实现装饰者的方法里,我们覆盖了原来对象的变量。使得所有调用对应方法的位置都会执行添加的函数,但是如果我们不是全都需要而是有的地方需要有的地方不需要呢?

我有两个思路,一个是在装饰类里重写方法时进行判断,另一个是把原来的对象单独给个变量,和装饰类加工过的对象有所区分,不需要执行新功能的地方,就用原来的对象调用。

感觉好像第一个思路好一点?

装饰函数

在js中,函数也是对象,只要是对象自然可以实现装饰者。

     var a=function(){
alert(1);
} var _a=a; //保存函数引用 a=function(){ //重写函数
_a(); //原本的函数引用
alert(2); //新添加的功能
}

又比如我们想给window绑定onload事件,但是又不确定这个事件是不是已经被其他人绑定过,为了避免覆盖掉之前的window.onload函数中的行为,我们一般都会先保存好原先的window.onload,把它放入新的window.onload 里执行:

     window.onload = function(){
alert (1);
}
var _onload = window.onload || function(){}; //没有绑过onload,让变量为空的函数 window.onload = function(){ //重写
_onload();
alert (2); //追加的代码
}

可能的坑:this

保存引用再重写,可能会带来this绑定错误的问题,因为我们保存的引用中的this和当前执行环境的this可能完全不同了,重写的函数有时候上下文其实已经改变了,如果上下文改变,this的指向也就改变了,所以它有可能没有指向正确的对象。

     var _getElementById = document.getElementById;  //这个是document这个对象的方法,其内部this,指向document,我们把它的引用保存到_getElementById这个全局变量里
document.getElementById = function( id ){ //重写函数
alert (1);
return _getElementById( id ); // 返回了保留的函数,但是返回的函数是全局函数,this指向window,没有指向正确的document对象
}
var button = document.getElementById( 'button' ); //报错,Uncaught TypeError: Illegal invocation

又比如:

     var name="大白";
var a={
name:"如花",
getname:function(){
console.log(this.name);
}
} var get=a.getname; //保存引用 get(); //大白
a.getname(); //如花

可以看到,调用函数时的环境改变,虽然保存了引用,却没得到我们想要的结果。(我们想获得如花)

如果遇到这种情况,也就只能手动更改(apply),或者控制在同一个上下文环境中添加职责。

比如上面的代码,我们改成:

    get.apply(a);  //如花

就可以获得正确的结果。

第一个例子也是同理

     var _getElementById = document.getElementById;
document.getElementById = function(){
alert (1);
return _getElementById.apply( document, arguments ); //apply大显身手,指定上下文为document
}
var button = document.getElementById( 'button' );

优缺点

装饰者模式可以说是AOP思想的实例。优点:解耦,追加功能不用修改原本函数,更为灵活方便。缺点:会叠加函数的作用域,如果装饰的链条过长,性能上也会受到一些影响。

《javascript设计模式与开发实践》阅读笔记(15)—— 装饰者模式的更多相关文章

  1. javascript设计模式与开发实践阅读笔记(4)——单例模式

    定义 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 具体来说,就是保证有些对象有且只有一个,比如线程池.全局缓存.浏览器中的window 对象等.在js中单例模式用途很广,比如登录 ...

  2. javascript设计模式与开发实践阅读笔记(8)——观察者模式

    发布-订阅模式,也叫观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript开发中,我们一般用事件模型来替代传统的观察者模式. ...

  3. javascript设计模式与开发实践阅读笔记(7)——迭代器模式

    迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  4. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

  5. javascript设计模式与开发实践阅读笔记(5)——策略模式

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...

  6. javascript设计模式与开发实践阅读笔记(9)——命令模式

    命令模式:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 说法很复 ...

  7. javascript设计模式与开发实践阅读笔记(11)—— 模板方法模式

    模板方法模式: 由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类.通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序.子类通过继承这个抽象类,也继 ...

  8. JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)

    说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...

  9. 《JavaScript设计模式与开发实践》笔记第八章 发布-订阅模式

    第八章 发布-订阅模式 发布-订阅模式描述 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于 ...

  10. JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)

    上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值, ...

随机推荐

  1. Luogu3444:[POI2006]ORK-Ploughing

    题意 见luogu Sol 贪心+枚举 如果知道最后一次是消除一行 那么一定消了\(n\)行 此时只要消的列最小就好了 枚举每列从上往下消到哪里,那么下面消的越小越好 那么就有了贪心策略: 先消左右的 ...

  2. Several ports (8005, 8080, 8009)被占用

    启动Tomcat服务器报错: Several ports (8005, 8080, 8009) required by Tomcat v5.5 Server at localhost are alre ...

  3. tensorflow 学习日志

    Windows安装anaconda 和 TensorFlow anaconda : https://zhuanlan.zhihu.com/p/25198543        anaconda 使用与说 ...

  4. Unity3D 心跳检测

    在B/S结构的项目开发的过程当中 在服务端与客户端正常的通信之外 服务端通常还需要知道客户端是否还处于连接状态 或者客户端也需要知道服务端是否还处在开启状态 大白话说完了,听一下比较正统的解释吧(摘自 ...

  5. 【OpenCV-Python:实现人脸、人眼、嘴巴识别】实战(一)

    AI时代的到来,手机上的APP开始应用人脸识别去完成事情,如iphoneX的人脸解锁,百度自动贩卖机的人脸识别系统进行支付,支付宝的人脸识别登录等,提高了使用软件的易用性,但也因为其便利性,在某些市面 ...

  6. 萌新关于C#委托一点见解

    开博第一写C#委托(一个简单的委托) 1.关于委托,一直是学习c#的萌新们的噩梦,小生也是.最近在学委托感觉瞬间被虐成狗,但作为C#中极为重要的一个内容,学好了将会及大地减少我们的代码量,而且这也是够 ...

  7. 新华三H3C服务器安装系统问题

    服务器装系统出现如下问题: 解决: 1)先点击Install Centos7进入系统盘系统,查看对应路径的盘符标识: 2)重启按e进入编辑界面修改对应的读取路径: 3)Ctrl+x继续进入,开始正常系 ...

  8. javascript中的null,对象系统还是非对象系统?

    1.一直以来的认知 在我学习js的过程中,爱民老师的绿皮书里将js的类型系统分成了两类: 其一是元类型系统:由typeof运算来检测 其二是对象类型系统:是元类型的object的一个分支 而null这 ...

  9. SpringBoot快速开发REST服务最佳实践

    一.为什么选择SpringBoot Spring Boot是由Pivotal团队提供的全新框架,被很多业内资深人士认为是可能改变游戏规则的新项目.早期我们搭建一个SSH或者Spring Web应用,需 ...

  10. markdown语法小结

    引用数学公式1 \[ \begin{equation} \pi^2=x^2+y \label{eq_lab1} \end{equation} \] Here we cite this equation ...