《javascript设计模式与开发实践》阅读笔记(15)—— 装饰者模式
装饰者模式
可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰者模式。
代码例子
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)—— 装饰者模式的更多相关文章
- javascript设计模式与开发实践阅读笔记(4)——单例模式
定义 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 具体来说,就是保证有些对象有且只有一个,比如线程池.全局缓存.浏览器中的window 对象等.在js中单例模式用途很广,比如登录 ...
- javascript设计模式与开发实践阅读笔记(8)——观察者模式
发布-订阅模式,也叫观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript开发中,我们一般用事件模型来替代传统的观察者模式. ...
- javascript设计模式与开发实践阅读笔记(7)——迭代器模式
迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...
- javascript设计模式与开发实践阅读笔记(6)——代理模式
代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...
- javascript设计模式与开发实践阅读笔记(5)——策略模式
策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...
- javascript设计模式与开发实践阅读笔记(9)——命令模式
命令模式:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 说法很复 ...
- javascript设计模式与开发实践阅读笔记(11)—— 模板方法模式
模板方法模式: 由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类.通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序.子类通过继承这个抽象类,也继 ...
- JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)
说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...
- 《JavaScript设计模式与开发实践》笔记第八章 发布-订阅模式
第八章 发布-订阅模式 发布-订阅模式描述 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于 ...
- JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)
上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值, ...
随机推荐
- webstorm修改工具栏主题
** 操作如图所示 **
- WordPress制作圆形头像友情链接页面的方法
网上看见过很多种友情链接页面,我比较喜欢的是圆形头像的这种,先看看效果吧:传送门 就是这种上面是圆形的友链用户头像,下面是友链用户网站名,然后鼠标移上去头像会旋转,怎么实现这种效果呢?我在网上找了很多 ...
- 【Unity与23种设计模式】适配器模式(Adapter)
GoF中定义: "将一个类的接口转换成为客户端期待的类接口.适配器模式让原本接口不兼容的类能一起合作." 适配器模式与装饰模式有一定的相似之处 两者都是在着手解决C#不能多继承的问 ...
- php提交表单时判断 if($_POST[submit])与 if(isset($_POST[submit])) 的区别
if(isset($_POST['submit'])) 它的意思是不是判断是否配置了$_POST['submit'] 这个变量呢?如果有这个变量 在执行其它代码 应该这样用if(isset($_POS ...
- shell总的exit与sleep
- Cesium polygon中的height和extrudedHeight的区别
1.height参数: 多边形和椭球表面之间的距离(以米为单位). 2.extrudedHeight参数: 多边形的挤压面与椭球面之间的距离(以米为单位).
- 教你快速打造PHP MVC框架
简介 MVC框架在现在的开发中相当流行,不论你使用的是JAVA,C#,PHP或者IOS,你肯定都会选择一款框架.虽然不能保证100%的开发语言都会使用框架,但是在PHP社区当中拥有最多数量的MVC框架 ...
- Java技术总结
1.在非空判断是一定把not null 判断写前边,否则如果为空先判断size为0会报错 String str = null; if(str !=null&&str.length()& ...
- JQ在光标处插入文字
内容转载自网络这是一个JQ的扩展方法.在teatarea获得焦点时,往光标处插入文字,扩展代码如下 (function($){ $.fn.extend({ "insert":fun ...
- VMware 下快速克隆出多个 Linux 环境
念念不忘,必有回响 好好工作,好好吃饭,困了倒头就睡:吃你认为好吃的,吃到饱:买贵的,你想买的:去玩去野: 就这样. 为什么要克隆多个 Linux 系统? 因为要玩阿. 其实也不是了,就是为了折腾嘛, ...