前言

装饰者模式和适配器模式都属于包装模式的不同实现,在适配器模式中,是需要将一个接口转变成另一个接口,达到重复使用接口的目的。而在装饰者模式中,并不改变原有接口,而是给原有接口添加额外的功能,使其变得更加强大。

什么是装饰者模式?

装饰者模式能够在不改变自身的情况下,动态添加功能,相比较继承,装饰者是一种更为灵活的用法。可以动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象;

传统面向对象的装饰者和JavaScript装饰者对比:

  1. 传统面向对象语言中的装饰者模式 ```javascript //模拟传统语言的装饰者

//原始的飞机类 var Plan = function () { };

Plan.prototype.fire = function () { console.log(‘发射普通子弹’); };

//装饰类 var MissileDecorator = function (plan) { this.plan = plan; }

MissileDecorator.prototype.fire = function () { this.plan.fire(); console.log(‘发射导弹!’); };

var plan = new Plan(); plan = new MissileDecorator(plan); plan.fire();

  1. 2. js中的装饰者模式
  2. 装饰者模式将一个对象嵌入到另一个对象之中,实际上相当于这个对象被另一个对像包装起来,形成一条包装链。请求随着这条包装链依次传递到所有的对象,每个对象都有处理这条请求的机会。
  3. ```javascript
  4. var Plan1 = {
  5. fire: function () {
  6. console.log('发射普通的子弹');
  7. }
  8. };
  9. var missileDecorator= function () {
  10. console.log('发射导弹!');
  11. };
  12. var fire = Plan1.fire;
  13. Plan1.fire=function () {
  14. fire();
  15. missileDecorator();
  16. };
  17. Plan1.fire();

在这里我们要注意的是,这种方法通过对对象方法重写来实现对原有方法扩展目的,但是有时候会导致this指向丢失问题。比如下面这个例子:

  1. var _getElementById = document.getElementById;
  2. document.getElementById = function( id ){
  3. alert (1);
  4. return _getElementById( id ); // (1)
  5. }
  6. var button = document.getElementById( 'button' );

执行这个方法,会在弹出1后报错,原因在于返回的_getElementById(id)该函数处于全局环境中,也就意味者在这里面this指向了window,然而跟据定义_getElementById(id)应该指向的是document这个对象。

完美实现装饰者模式

前面我们简单的将对象赋值给一个对象保存,然后再改变这个对象,但是this引用却没有保存下来,就导致this丢失问题。所以我们就需要解决this丢失问题。

  1. var before = function(fn,beforefn){
  2. return function(){
  3. beforefn.apply(this,arguments);
  4. return fn.apply(this,arguments);
  5. }
  6. }
  7. var after = function(fn,afterfn){
  8. return function(){
  9. const ret = fn.apply(this,arguments);
  10. afterfn.apply(this,arguments);
  11. return ret
  12. }
  13. }
  14. //使用案例
  15. let a = before(document.getElementById( 'button' ),function(){
  16. alert (1);
  17. })
  18. a.call(document,"ReactApp")

在这里主要作用是需要保存this指向,也就是说在调用这个函数的时候需要非常明确知道this到底指向那里,幸运的是我们也有一种方法不需要知道this指向哪里也就是将before和after方法添加到function原型链上,这个希望你们自己能够去实现。