一、前言:

装饰者模式(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属性,并指向函数AbstractDecorator this.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对象赋给tree
tree.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 --> 空对象tree
tree.decorate();
分析到这里,就不难知道最后的输出结果了。
三、其他:
我们可以看出本文章中的装饰者模式案例中用了很多this,对this不太了解的朋友可以移步到 《深入理解javascript中的 “this”》。
本文案例建议复制下来逐行分析,赶紧行动起来吧!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
# Javascript
# 设计模式
# 装饰者模式
# JavaScript装饰者模式原理与用法实例详解
# javascript设计模式之装饰者模式
# 新手快速入门JavaScript装饰者模式与AOP
# JavaScript设计模式之装饰者模式实例详解
# JavaScript设计模式之装饰者模式定义与应用示例
# 轻松掌握JavaScript装饰者模式
# 学习JavaScript设计模式之装饰者模式
# JavaScript设计模式之装饰者模式介绍
# JS装饰者模式和TypeScript装饰器
# 可以看出
# 这时候
# 象中
# 用了
# 第二个
# 其中一个
# 这行
# 不改变
# 是一个
# 情况下
# 如果你
# 尤其是
# 不太
# 这么多
# 说到
# 我们可以
# 再来
# 很重要
# 当我们
# 成为一个
相关文章:
h5网站制作工具有哪些,h5页面制作工具有哪些?
设计网站制作公司有哪些,制作网页教程?
视频网站app制作软件,有什么好的视频聊天网站或者软件?
如何快速重置建站主机并恢复默认配置?
如何在Windows环境下新建FTP站点并设置权限?
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
建站IDE高效指南:快速搭建+SEO优化+自适应模板全解析
外贸公司网站制作,外贸网站建设一般有哪些步骤?
如何通过PHP快速构建高效问答网站功能?
定制建站流程解析:需求评估与SEO优化功能开发指南
网站制作的软件有哪些,制作微信公众号除了秀米还有哪些比较好用的平台?
建站上市公司网站建设方案与SEO优化服务定制指南
如何制作算命网站,怎么注册算命网站?
javascript中对象的定义、使用以及对象和原型链操作小结
广平建站公司哪家专业可靠?如何选择?
如何通过宝塔面板实现本地网站访问?
Python多线程使用规范_线程安全解析【教程】
微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?
黑客入侵网站服务器的常见手法有哪些?
建站之星代理平台如何选择最佳方案?
新网站制作渠道有哪些,跪求一个无线渠道比较强的小说网站,我要发表小说?
建站之星安装后如何配置SEO及设计样式?
平台云上自主建站:模板化设计与智能工具打造高效网站
湖南网站制作公司,湖南上善若水科技有限公司做什么的?
如何通过西部数码建站助手快速创建专业网站?
建站DNS解析失败?如何正确配置域名服务器?
招贴海报怎么做,什么是海报招贴?
如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?
Dapper的Execute方法的返回值是什么意思 Dapper Execute返回值详解
小说建站VPS选用指南:性能对比、配置优化与建站方案解析
东莞专业制作网站的公司,东莞大学生网的网址是什么?
手机网站制作与建设方案,手机网站如何建设?
详解jQuery中基本的动画方法
个人网站制作流程图片大全,个人网站如何注销?
建站主机如何选?性能与价格怎样平衡?
,购物网站怎么盈利呢?
建站之星后台密码遗忘?如何快速找回?
建站之星后台搭建步骤解析:模板选择与产品管理实操指南
沈阳制作网站公司排名,沈阳装饰协会官方网站?
建站之星价格显示格式升级,你的预算足够吗?
C#如何使用XPathNavigator高效查询XML
网站规划与制作是什么,电子商务网站系统规划的内容及步骤是什么?
模具网站制作流程,如何找模具客户?
清单制作人网站有哪些,近日“兴风作浪的姑奶奶”引起很多人的关注这是什么事情?
在线教育网站制作平台,山西立德教育官网?
如何在服务器上三步完成建站并提升流量?
如何在IIS中新建站点并配置端口与IP地址?
html制作网站的步骤有哪些,iapp如何添加网页?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
*请认真填写需求信息,我们会在24小时内与您取得联系。