当前位置:首页 > 日记 > 正文

Javascript设计模式之装饰者模式详解篇

Javascript设计模式之装饰者模式详解篇

一、前言:

装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象。

装饰者模式的特点:

1. 在不改变原对象的原本结构的情况下进行功能添加。

2. 装饰对象和原对象具有相同的接口,可以使客户以与原对象相同的方式使用装饰对象。

3. 装饰对象中包含原对象的引用,即装饰对象是真正的原对象经过包装后的对象。

二、Javascript装饰者模式详解:

描述:

装饰者模式中,可以在运行时动态添加附加功能到对象中。当处理静态类时,这可能是一个挑战。在Javascript中,由于对象是可变的,因此,添加功能到对象中的过程本身并不是问题。

装饰者模式的一个比较方便的特征在于其预期行为的可定制和可配置特性。可以从仅具有一些基本功能的普通对象开始,然后从可用装饰资源池中选择需要用于增强普通对象的哪些功能,并且按照顺序进行装饰,尤其是当装饰顺序很重要的时候。

实现装饰者模式的其中一个方法是使得每个装饰者成为一个对象,并且该对象包含了应该被重载的方法。每个装饰者实际上继承了目前已经被前一个装饰者进行增强后的对象。每个装饰方法在“继承的对象”上调用了同样的方法并获取其值,此外它还继续执行了一些操作。

先上实例1:

//需要装饰的类(函数)function Macbook() { this.cost = function () {  return 1000; };} //计算商品的包装费function PackagingFee(macbook) { this.cost = function () {  return macbook.cost() + 75; };}//计算商品的运费function Freight(macbook) { this.cost = function () {  return macbook.cost() + 300; };} //计算商品的保险费用function Insurance(macbook) { this.cost = function () {  return macbook.cost() + 250; };}// 用法var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook())));console.log(myMacbook.cost());//1625

我们简单的分析下上面的代码,上面的代码中,一共定义了四个函数(其中一个需要修饰的函数,三个用于修饰的函数)。

然后,声明一个变量myMacbook指向new出来的Insurance对象,Insurance对象的形参指向new出来的Freight对象,Freight对象的形参指向new出来的PackagingFee对象,PackagingFee对象的形参指向new出来的Macbook对象。

接下来,调用myMacbook的cost方法。从上面的分析,我们可以得出 myMacbook.cost()的值等于(Freight对象的cost方法+250),Freight对象的cost方法等于(PackagingFee对象的cost方法+300),PackagingFee对象的cost方法等于(Macbook对象的cost方法+75)。

所以最终的结果是:myMacbook.cost()的值 = 250 + (300 + (75 + 1000)) = 1625。

// 用法var myMacbook = new Insurance(new Freight(new PackagingFee(new Macbook())));console.log(myMacbook.cost());//1625 //上面的代码等价于下面拆分后的代码,或许拆分后代码你更能看出前后的逻辑性var macbook = new Macbook();var package = new PackagingFee(macbook);var freight = new Freight(package);var myMacbook = new Insurance(freight);//当然,如果你不想声明这么多变量(macbook、package、freight),只用一个变量也是可以的var macbook = new Macbook();macbook = new PackagingFee(macbook);macbook = new Freight(macbook);var myMacbook = new Insurance(macbook);

再看看实例2:

function ConcreteClass() { this.performTask = function () {  this.preTask();  console.log('doing something');  this.postTask(); };}function AbstractDecorator(decorated) { this.performTask = function () {  decorated.performTask(); };}function ConcreteDecoratorClass(decorated) { this.base = AbstractDecorator; this.base(decorated);// add performTask method decorated.preTask = function () {  console.log('pre-calling..'); }; decorated.postTask = function () {  console.log('post-calling..'); };}var concrete = new ConcreteClass();var decorator1 = new ConcreteDecoratorClass(concrete);decorator1.performTask();//pre-calling..//doing something//post-calling..

实例2实际上和实例1是非常类似的,我们来简单分析下吧。首先,实例2中定义了三个函数,然后声明了两个变量concrete和decorator1,最后调用了decorator1的performTask方法。

粗看一眼,ConcreteDecoratorClass里面好像并没有performTask方法。我们先来分析下面的两行代码:

var concrete = new ConcreteClass(); //声明一个变量concrete指向new出来的ConcreteClass对象var decorator1 = new ConcreteDecoratorClass(concrete); //声明一个变量decorator1指向new出来的ConcreteDecoratorClass对象,并传入变量concrete作为形参

然后,我们再来逐行分析下ConcreteDecoratorClass函数里面的代码:

this.base = AbstractDecorator; //定义一个当前对象(decorator1)的base属性,并指向函数AbstractDecoratorthis.base(decorated); //调用base属性指向的函数,也就是调用AbstractDecorator函数,同时传入形参decorated,形参decorated指向new出来的ConcreteClass对象

说到这里,好像还是没有分析出ConcreteDecoratorClass函数里面有performTask方法,重点是看 "this"!

ConcreteDecoratorClass函数中的this指向new出来的ConcreteDecoratorClass对象(也就是和decorator1指向同一个对象);

AbstractDecorator函数里面的this关键是看哪个对象来调用这个函数,this就指向哪个对象(从代码 “this.base = AbstractDecorator; this.base(decorated);” 中我们可以看出是new出来的ConcreteDecoratorClass对象在调用AbstractDecorator函数),所以AbstractDecorator函数里面的this指向new出来的ConcreteDecoratorClass对象(也和decorator1指向同一个对象)。

总结下来,我们会发现,在上面的代码中,不管是ConcreteDecoratorClass函数里面的this,还是AbstractDecorator函数里面的this,都指向new出来的ConcreteDecoratorClass对象。

所以,当我们执行decorator1.performTask()时,它会继续执行匿名函数中的代码(decorated.performTask();),匿名函数中的decorated形参指向new出来的ConcreteClass对象,并执行该对象的performTask方法。

最后看看实例3:

var tree = {};tree.decorate = function () { console.log('Make sure the tree won\'t fall');}; tree.getDecorator = function (deco) { tree[deco].prototype = this; return new tree[deco];}; tree.RedApples = function () { this.decorate = function () {  this.RedApples.prototype.decorate(); // 第7步:先执行原型(这时候是Angel了)的decorate方法  console.log('Add some red apples'); // 第8步 再输出 red  // 将这2步作为RedApples的decorate方法 }};tree.BlueApples = function () { this.decorate = function () {  this.BlueApples.prototype.decorate(); // 第1步:先执行原型的decorate方法,也就是tree.decorate()  console.log('Put on some blue apples'); // 第2步 再输出blue  // 将这2步作为BlueApples的decorate方法 }}; tree.Angel = function () { this.decorate = function () {  this.Angel.prototype.decorate(); // 第4步:先执行原型(这时候是BlueApples了)的decorate方法  console.log('An angel on the top'); // 第5步 再输出angel  // 将这2步作为Angel的decorate方法 }};tree = tree.getDecorator('BlueApples'); // 第3步:将BlueApples对象赋给tree,这时候父原型里的getDecorator依然可用tree = tree.getDecorator('Angel'); // 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用tree = tree.getDecorator('RedApples'); // 第9步:将RedApples对象赋给treetree.decorate(); // 第10步:执行RedApples对象的decorate方法//Make sure the tree won't fall//Add blue apples//An angel on the top//Put on some red apples

实例3看起来很复杂,实际上分析逻辑还是和前面两个实例一样,我们可以看出实例3中一共声明了5个函数表达式。我们重点分析下下面的代码:

//tree.getDecorator('BlueApples')返回new出来的tree.BlueApples的实例对象,并将该对象赋值给空的tree对象tree = tree.getDecorator('BlueApples'); //new出来的tree.BlueApples的实例对象的原型指向 --> 空对象tree //tree.getDecorator('Angel')返回new出来的tree.Angel的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.BlueApples的实例对象)tree = tree.getDecorator('Angel'); //new出来的tree.Angel的实例对象的原型指向 --> tree.BlueApples的实例对象//tree.getDecorator('RedApples')返回new出来的tree.RedApples的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.Angel的实例对象)tree = tree.getDecorator('RedApples'); //new出来的tree.RedApples的实例对象的原型指向 --> tree.Angel的实例对象//调用tree.decorate(),这里的tree已经是new出来的tree.RedApples的实例对象了。//tree.RedApples的实例对象的decorate属性方法里面的第一行代码是 “this.RedApples.prototype.decorate()”//结合上面的分析可以得出以下的原型链结构://this.RedApples.prototype --> tree.Angel;//tree.Angel.prototype --> tree.BlueApples;//tree.BlueApples.prototype --> 空对象treetree.decorate();

分析到这里,就不难知道最后的输出结果了。

三、其他:

我们可以看出本文章中的装饰者模式案例中用了很多this,对this不太了解的朋友可以移步到 《深入理解javascript中的 “this”》。

本文案例建议复制下来逐行分析,赶紧行动起来吧!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!

相关文章

excel2007设置共享模式的方法excel

excel2007设置共享模式的方法excel

模式,设置,方法,如何设置,电脑软件,  Excel中经常需要使用到共享模式进行多人录入,共享模式具体该如何设置出来呢?下面是由小编分享的excel2007设置共享模式的方法,以供大家阅读和学习。excel2007设置共享模式的方法设置共享模式步骤1:首先…

ps怎样把人物照片制作成一寸红底证

ps怎样把人物照片制作成一寸红底证

照片,一寸,人物,证件照,电脑软件,ps怎样把人物照片制作成一寸红底证件照?运用PHOTOSHOP软件,可以很简单的制作大家想要的照片,下面和小编一起来看看具体步骤吧。最终效果 具体步骤:打开一张证件照片,首先在【图像】选项卡里面查看一下的大小。…

简单谈谈Javascript函数中的argume

简单谈谈Javascript函数中的argume

函数,简单,电脑软件,Javascript,arguments,一、arguments的面貌在javascript中所有的函数内部都包含了一个隐藏的变量叫arguments;它存放着所有传递到这个函数中的参数;那么我们打开实例看看arguments的输出形式(function fn(){ console.log…

用 js 的 selection range 操作选

用 js 的 selection range 操作选

区域,选择,操作,内容,电脑软件,先放上最后的效果,这是点击图片以前:这是点击图片以后:非常清晰,一目了然,就算是小白用户也明白发生了什么。挺好的。最近在做编辑器相关的东西,遇到一个需求,用户在编辑器中插入或者粘贴的图片要支持点击以后,可以按…

ps怎么制作一层一层延伸的洞穴特效

ps怎么制作一层一层延伸的洞穴特效

洞穴,特效,电脑软件,ps,巧用photoshop的滤镜功能,可以制作许多意想不到的特效图片,这里笔者以介绍一款迷幻洞穴的制作方法,简要介绍滤镜的使用功能。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB更新时间:2015-11-041、首先…

Photoshop设计制作漂亮的春季小清

Photoshop设计制作漂亮的春季小清

设计制作,春季,海报,漂亮,电脑软件,作者制作电商海报非常有经验,前期需要找一些同类的产品海报进行参考;然后根据自身产品特点构思画面并画出草图;再搜集素材;后期溶图,调色,并增加文案即可。最终效果1、轮廓图。2、下面是根据自己的构思找到的素…

JS异步加载的三种实现方式

JS异步加载的三种实现方式

异步加载,三种,方式,电脑软件,JS,js加载的缺点:加载工具方法没必要阻塞文档,过多js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作。 有些工具方法需要按需加载,用到再加载,不用不加载,。默认正常模式下下,JS是…

JS鼠标滚动分页效果示例

JS鼠标滚动分页效果示例

鼠标,分页,示例,效果,电脑软件,首先先看问题:在开发的时候,看到这种现象 就会思考:为什么左边的数据出来比右边的慢呢?因为这里没有进行分页,左边的数据多,所以查询相对较慢。解决办法就是进行分页,但是在项目中用到的插件,不能控制样式,改变分页的…

excel函数值不可用错误怎么解决解

excel函数值不可用错误怎么解决解

函数,错误,方法,解决方法,不可用,  excel的使用过程中经常会出现一些错误,那么值不可用错误就是其中的一种,很常见,那怎么解决这个问题呢?一般情况下,一部分原因是引用的数据是错的,一部分是因为公式的问题,下面给大家分享excel函数值不可用错…

微信小程序实现倒计时60s获取验证

微信小程序实现倒计时60s获取验证

验证码,倒计时,程序,电脑软件,微信小,本文实例为大家分享了微信小程序倒计时获取验证码的具体代码,供大家参考,具体内容如下1、工具类(引用微信小程序提供的工具类)countdown.jsclass Countdown {constructor(options = {}) {Object.assign(thi…

YII2自动登录Cookie总是失效的解决

YII2自动登录Cookie总是失效的解决

失效,解决方法,自动登录,电脑软件,Cookie,前言最近做Yii2自动登录功能,发现即使开启了Yii2的自动登录配置功能,浏览器关闭后,再次打开浏览器还是处于非登录状态。网上查询资料基本没有相同情况。查询登录源码:protected function sendIdentityC…

PS怎么制作清明节字体海报?

PS怎么制作清明节字体海报?

字体,清明节,海报,电脑软件,PS,清明节到了,各路商家或宣传都想设计一个美美的清明节海报,本经验将介绍如何设计一个清明节海报,希望能够帮助读者。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB更新时间:2015-11-041、【打开…