一、前言:

装饰者模式(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设计模式之装饰者模式详解篇的更多相关文章

  1. (十)装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...

  2. 涉及模式之 装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...

  3. 再起航,我的学习笔记之JavaScript设计模式13(装饰者模式)

    装饰者模式 装饰者模式(Decorator): 在不改变原对象的基础上,通过对其进行过包装拓展(添加属性高或者方法)使原有对象可以满足用户的更复杂需求. 如果现在我们有个需求,需要做一个提交表单,当我 ...

  4. Java 装饰器模式详解

    转载请注明出处:http://blog.csdn.net/zhaoyanjun6/article/details/56488020 前言 在上面的几篇文章中,着重介绍了Java 中常见的 IO 相关知 ...

  5. JavaScript设计模式-17.装饰者模式(下)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. JavaScript设计模式-16.装饰者模式(上)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. JavaScript设计模式(装饰者模式)

    一.模拟传统面向对象语言的装饰者模式: 假设我们在编写一个飞机大战的游戏,随着经验值的增加,我们操作的飞机对象可以升级成更厉害的飞机,一开始这些飞机只能发射普通的子弹,升到第二级时可以发射导弹,升到第 ...

  8. JavaScript设计模式(8)-装饰者模式

    装饰者模式 1. 作用: 可用来透明地把对象包装在具有同样接口的另一对象之中,这样可以给一个方法添加一些行为,然后将方法调用传递给原始对象. 可用于为对象增加功能,用来代替大量子类. 装饰者对其组件进 ...

  9. javascript设计模式之装饰者模式

    /* * 装饰者模式提供比继承更有弹性的替代方案 * 在不改变原构造函数的情况下,添加新的属性或功能 */ //需要装饰的类(函数) function Macbook() { this.cost = ...

随机推荐

  1. SQL点滴21—几个有点偏的语句

    原文:SQL点滴21-几个有点偏的语句 SQL语句是一种集合操作,就是批量操作,它的速度要比其他的语言快,所以在设计的时候很多的逻辑都会放在sql语句或者存储过程中来实现,这个是一种设计思想.但是今天 ...

  2. 文件合并工具DiffMerge发布4.2版本

    DiffMerge一直是文件对比合并工具的佼佼者,其最大特点是多文件对比与合并,并提供可视化界面用于编辑. 此次DiffMerge v4.2发布,提高了文件差异对比,并提供了快速匹配功能,以及更好的用 ...

  3. js实现tooltip动态提示效果(文字版)

    页面中经常用到鼠标移动到一个元素上面显示提示的功能,最开始的做法是在下面创建一个div然后动态显示这个div,但是这样需要加很多div,比较麻烦. 针对上面个的需求,这边写了一个tooltip动态提示 ...

  4. 解决Shockwave flash在chrome该浏览器崩溃

    越来越多的人开始使用chrome浏览器,很多用户都遇到过flash崩溃.有时重启chrome为了解决,有时不可能使用chrome无论打开什么网站是什么flash.这个问题是非常小的Firefox或IE ...

  5. MVC 控制器激活

    MVC 控制器激活 ASP.NET MVC 控制器激活(三) 前言 在上个篇幅中说到从控制器工厂的GetControllerInstance()方法来执行控制器的注入,本篇要讲是在GetControl ...

  6. 初识Identity

    初识Identity 摘要 通过本文你将了解ASP.NET身份验证机制,表单认证的基本流程,ASP.NET Membership的一些弊端以及ASP.NET Identity的主要优势. 目录 身份验 ...

  7. WCF的简单

    WCF的简单 WCF的学习之旅 一.WCF的简单介绍  Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows ...

  8. Coded UI Test对Webpage进行自动化测试

    如何使用Coded UI Test对Webpage进行自动化测试   在Visual Studio中,Coded UI Test已经不是什么新特性了,较早版本的Visual Studio中就已经有这个 ...

  9. 【分享】LateX排版软件学习教程合集

    来源于:http://www.hejizhan.com/html/xueke/416/x416_13.html  LATEX2e科技排版指南.pdf 8.3 MB  An Example LaTeX ...

  10. 今天用C#做的一个小的注册练习

    下边是实现的代码: using System;using System.Collections.Generic;using System.ComponentModel;using System.Dat ...