资讯专栏INFORMATION COLUMN

javascript设计模式--装饰者模式

haoguo / 2316人阅读

摘要:装饰者模式定义装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。

装饰者模式 定义 :

装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。

在不改变对象自身的基础上,在程序运行期间给对象动态地添加一些额外职责

特点 :

可以动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象;

作用:

装饰者模式的作用就是为对象动态的加入某些行为。

为什么要使用装饰者模式

在传统面向对象语言中,为对象添加功能常使用继承
但是继承有很多缺点:

超类子类强耦合,超类改变导致子类改变
超类内部细节对子类可见,破坏了封装性
完成功能复用同时,可能会创造大量子类

举例:

</>复制代码

  1. var person = {
  2. name: "payen",
  3. sex: "male"
  4. }
  5. person.age = "20";

装饰者模式是在不改变对象自身的基础上
而我们改变了原对象 ,所以上面这个例子不是装饰器

示例:

</>复制代码

  1. /*模拟传统语言的装饰者
  2. 装饰者模式将一个对象嵌入到另一个对象之中,
  3. 实际上相当于这个对象被另一个对像包装起来,形成一条包装链。
  4. 请求随着这条包装链依次传递到所有的对象,每个对象都有处理这条请求的机会。
  5. */
  6. //原始的飞机类
  7. var Plan = function () {
  8. };
  9. Plan.prototype.fire = function () {
  10. console.log("发射普通子弹");
  11. };
  12. //装饰类
  13. var MissileDecorator = function (plan) {
  14. this.plan = plan;
  15. };
  16. MissileDecorator.prototype.fire = function () {
  17. this.plan.fire();
  18. console.log("发射导弹!");
  19. };
  20. var plan = new Plan();
  21. plan = new MissileDecorator(plan);
  22. plan.fire();

这样给对象动态的增加职责的方式就没有改变对象自身
一个对象放入另一个对象
形成了一条装饰链(一个聚合对象)
而上面的shot和track也就是是装饰者、装饰函数
当函数执行时,会把请求转给链中的下一个对象

函数功能扩展

在JavaScript中,很容易给对象扩展属性与方法
但是却不容易给函数扩展额外功能,除非改函数源码
但是改写函数违反了开放-封闭原则

</>复制代码

  1. var foo = function(){
  2. console.log(1);
  3. }
  4. //改为
  5. var foo = function(){
  6. console.log(1);
  7. console.log(2);//增
  8. }

一个常用的方法就是缓存函数引用,改写函数

</>复制代码

  1. var foo = function(){
  2. console.log(1);
  3. }
  4. //改为
  5. var foo = function(){
  6. console.log(1);
  7. }
  8. var _foo = foo;
  9. foo = function(){
  10. _foo();
  11. console.log(2);
  12. }

但是这样写还是存在问题

*要维护额外的中间变量(_foo),如果装饰链过长,中间变量就会越来越多
可能会存在this被劫持问题*
关于this劫持问题,看下面的例子:

</>复制代码

  1. var getId = document.getElementById;
  2. document.getElementById = function(ID){
  3. console.log(1);
  4. return getId(ID);
  5. }
  6. document.getElementById("demo");

因为使用 document.getElementById 的时候
内部有this引用,而这个this期望指向的是document
但是 getId 在获取了 document.getElementById 引用后
this就指向了window,导致抛出错误

为了让this正确指向document
我们可以做出修改:

</>复制代码

  1. var getId = document.getElementById;
  2. document.getElementById = function(ID){
  3. console.log(1);
  4. return getId.call(document, ID);
  5. }
  6. document.getElementById("demo");
AOP装饰函数

</>复制代码

  1. AOP(Aspect Oriented Programming)面向切面编程
    把一些与核心业务逻辑无关的功能抽离出来
    再通过“动态织入”方式掺入业务逻辑模块

与业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等等
好处也很明显,保证了核心业务模块的纯净与高内聚性
而且其他的功能模块也可以很好的复用
首先我们要实现两个函数
一个用来前置装饰,一个用来后置装饰:

</>复制代码

  1. Function.prototype.before = function(beforeFunc){
  2. var that = this;
  3. return function(){
  4. beforeFunc.apply(this, arguments);
  5. return that.apply(this, arguments);
  6. }
  7. }
  8. Function.prototype.after = function(afterFunc){
  9. var that = this;
  10. return function(){
  11. var ret = that.apply(this, arguments);
  12. afterFunc.apply(this, arguments);
  13. return ret;
  14. }
  15. }

以前置装饰为例
调用before时,先把原函数的引用保存下来
然后返回一个“代理”函数
这样在原函数调用前,先执行扩展功能的函数
而且他们共用同一个参数列表
后置装饰与前置装饰基本类似,只是执行顺序不同

如果不喜欢这种污染原型的方式,也可以这么写:

</>复制代码

  1. var before = function(originFunc, beforeFunc){
  2. return function(){
  3. before.apply(this, arguments);
  4. return originFunc.apply(this, arguments);
  5. }
  6. }
  7. var after = function(originFunc, afterFunc){
  8. return function(){
  9. var ret = originFunc.apply(this, arguments);
  10. afterFunc.apply(this, arguments);
  11. return ret;
  12. }
  13. }

使用这种AOP的方式可以完美的对函数进行功能扩展

</>复制代码

  1. var foobar = function(x, y, z){
  2. console.log(x, y, z);
  3. }
  4. var foo = function(x, y, z){
  5. console.log(x/10, y/10, z/10);
  6. }
  7. var bar = function(x, y, z){
  8. console.log(x*10, y*10, z*10);
  9. }
  10. foobar = foobar.before(foo).after(bar);
  11. foobar(1, 2, 3);
  12. //0.1 0.2 0.3
  13. //1 2 3
  14. //10 20 30

由于原函数和装饰函数共用一个参数列表
所以我们可以用AOP改变函数参数

</>复制代码

  1. var data = {
  2. width: "100px",
  3. height: "100px"
  4. }
  5. var demo = function(data){
  6. console.log(JSON.stringify(data));
  7. }
  8. demo = demo.before(function(data){
  9. data.color = "red";
  10. });
  11. demo(data);
  12. //{"width":"100px","height":"100px","color":"red"}

最后谈一谈装饰者模式的缺点
它也不是十全十美的

装饰链叠加了函数作用域,如果过长也会产生性能问题
如果原函数上保存了属性,返回新函数后属性会丢失

</>复制代码

  1. var demo = function(){
  2. console.log(1);
  3. }
  4. demo.a = 123;
  5. demo = demo.after(function(){
  6. console.log(2);
  7. });
  8. demo();
  9. console.log(demo.a);
  10. //undefined

装饰者模式在开发中非常有用,在框架开发中也十分有用

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88798.html

相关文章

  • JavaScript设计模式----装饰模式

    摘要:声明这个系列为阅读设计模式与开发实践曾探著一书的读书笔记装饰者模式的定义装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。装饰者模式的作用就是为对象动态的加入某些行为。 声明:这个系列为阅读《JavaScript设计模式与开发实践》 ----曾探@著一书的读书笔记 装饰者模式的定义: 装饰者(decorator)模式能...

    rose 评论0 收藏0
  • 5 分钟即可掌握的 JavaScript 装饰模式与 AOP

    摘要:下装饰者的实现了解了装饰者模式和的概念之后,我们写一段能够兼容的代码来实现装饰者模式原函数拍照片定义函数装饰函数加滤镜用装饰函数装饰原函数这样我们就实现了抽离拍照与滤镜逻辑,如果以后需要自动上传功能,也可以通过函数来添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是装饰者模式 当我们拍了一张照片准备发朋友...

    chunquedong 评论0 收藏0
  • javascript设计模式 --- 装饰模式

    摘要:设计模式装饰者模式何为装饰者,意思就是,在不影响对象主功能的情况下,再添加一些额外的功能,使对象具备更多的功能。与继承相比,装饰者是一种更灵活轻便的做法。 javascript设计模式 --- 装饰者模式 何为装饰者,意思就是,在不影响对象主功能的情况下,再添加一些额外的功能,使对象具备更多的功能。与继承相比,装饰者是一种更灵活轻便的做法。下面我们看看javascript里装饰者模式 ...

    kumfo 评论0 收藏0
  • JavaScript设计模式七:装饰模式

    摘要:装饰者模式装饰者模式提供比继承更有弹性的替代方案。装饰者用于包装同接口的对象,用于通过重载方法的形式添加新功能,该模式可以在被装饰者的前面或后面加上自己的行为以达到特定的目的。简单的理解给对象动态添加职责的方式称为装饰着模式。 装饰者模式 装饰者模式提供比继承更有弹性的替代方案。装饰者用于包装同接口的对象,用于通过重载方法的形式添加新功能,该模式可以在被装饰者的前面或后面加上自己的行为...

    alexnevsky 评论0 收藏0
  • JavaScript 模式》知识点小抄本(上)

    摘要:单体模式有以下优点用来划分命名空间,减少全局变量数量。通常我们使用操作符创建单体模式的三种选择,让构造函数总返回最初的对象使用全局对象来存储该实例不推荐,容易全局污染。实现该工厂模式并不困难,主要是要找到能够穿件所需类型对象的构造函数。 介绍 最近开始给自己每周订个学习任务,学习结果反馈为一篇文章的输出,做好学习记录。 这一周(02.25-03.03)我定的目标是《JavaScri...

    didikee 评论0 收藏0

发表评论

0条评论

haoguo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<