面试官:说说你对装饰者模式的理解?应用场景?

3970141a9a3ab673fbff90e02fb0a154.png

一、是什么

装饰者模式(Decorator Pattern)就是动态的给类或对象增加职责的设计模式。它能在不改变类或对象自身的基础上,在程序的运行期间动态的添加职责,跟继承相比,装饰者是一种更轻便灵活的做法

这种设计模式非常符合敏捷开发的设计思想:先提炼出产品的最小可用产品,再通过快速迭代的方式添加功能

在生活中,同一张图片,组合不同的滤镜就会有不同的体验

这里实际上就应用了装饰者模式:是通过滤镜装饰了照片。在不改变对象(照片)的情况下动态的为其添加功能(滤镜)

二、使用

es6上,增添了类的装饰器,用来注释或修改类和类方法,使用类的装饰器实际上就应用了装饰者模式(关于类装饰器的使用这里不再陈述)如下:

f92fc9c4e45c4edce85b18c896787a39.png

JavaScript 中可以很方便地给某个对象扩展属性和方法,但却很难在不改动某个函数源代码的情况下,给该函数添加一些额外的功能

要想为函数添加一些功能,最简单粗暴的方式就是直接改写该函数,但这是最差的办法,直接违反了开放-封闭原则,如下:

var a = function () {
  alert(1);
}
// 改成
var a = function () {
  alert(1);
  alert(2);
}

实际开发中,比如我们想给 window 绑定 onload 事件,但是又不确定这个事件是不是已经被其他人绑定过,为了避免覆盖掉之前的 window.onload 函数中的行为,我们一般都会先保存好原先的 window.onload,把它放入新的 window.onload 里执行

window.onload = function () {
  alert(1);
}
var _onload = window.onload || function () { };
window.onload = function () {
  _onload();
  alert(2);
}

同样,我们还可以使用AOP 面向切面编程来装饰函数

什么是面向切面编程?举个例子,餐前洗手、饭后漱口,吃饭这个动作相当于切点,我们可以在这个切点前、后插入其它如洗手等动作

下面则使用AOP来修饰onLoad

首先定义Function.prototype.before 方法和 Function.prototype.after 方法,如下:

Function.prototype.before = function (beforefn) {
  var __self = this; // 保存原函数的引用
  return function () { // 返回包含了原函数和新函数的"代理"函数
    beforefn.apply(this, arguments); // 执行新函数,且保证 this 不被劫持,新函数接受的参数也会被原封不动地传入原函数,新函数在原函数之前执行
    return __self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,并且保证 this 不被劫持
  }
}
Function.prototype.after = function (afterfn) {
  var __self = this;
  return function () {
    var ret = __self.apply(this, arguments);
    afterfn.apply(this, arguments);
    return ret;
  }
};

Function.prototype.before 接受一个函数当作参数,这个函数即为新添加的函数,它装载了新添加的功能代码

它的工作是把请求分别转发给新添加的函数和原函数,且负责保证它们的执行顺序,让新添加的函数在原函数之前执行(前置装饰),这样就实现了动态装饰的效果。通过 Function.prototype.apply 来动态传入正确的 this,保证了函数在被装饰之后,this 不会被劫持

Function.prototype.after 的原理跟 Function.prototype.before 一模一样,唯一不同的地方在于让新添加的函数在原函数执行之后再执行

修改后代码如下:

window.onload = function () {
  alert(1);
}
window.onload = (window.onload || function () { }).after(function () {
  alert(2);
}).after(function () {
  alert(3);
}).after(function () {
  alert(4);
});

三、总结

应用场景:

适合对已拥有必备组件的对象进行扩展高阶组件或属性

优点:
  • 无需修改现有对象,也无需创建对象的子类,即可扩展对象的功能。

  • 可以多扩展功能进行动态添加或删除

  • 可以扩展不同的装饰者来解决扩展不同功能的问题

参考文献

  • https://juejin.cn/post/6844903873262387208

  • https://juejin.cn/post/6999826902642851854

  • https://juejin.cn/post/6844904100144889864

--The End--

系列正在更新:13/14

点击下方卡片解锁更多

6b492fcbba0d381382f93b781d13578f.png

创作不易,星标、点赞、在看 三连支持

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值