前言
装饰者模式和适配器模式都属于包装模式的不同实现,在适配器模式中,是需要将一个接口转变成另一个接口,达到重复使用接口的目的。而在装饰者模式中,并不改变原有接口,而是给原有接口添加额外的功能,使其变得更加强大。
什么是装饰者模式?
装饰者模式能够在不改变自身的情况下,动态添加功能,相比较继承,装饰者是一种更为灵活的用法。可以动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象;
传统面向对象的装饰者和JavaScript装饰者对比:
- 传统面向对象语言中的装饰者模式 ```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();
2. js中的装饰者模式装饰者模式将一个对象嵌入到另一个对象之中,实际上相当于这个对象被另一个对像包装起来,形成一条包装链。请求随着这条包装链依次传递到所有的对象,每个对象都有处理这条请求的机会。```javascriptvar Plan1 = {fire: function () {console.log('发射普通的子弹');}};var missileDecorator= function () {console.log('发射导弹!');};var fire = Plan1.fire;Plan1.fire=function () {fire();missileDecorator();};Plan1.fire();
在这里我们要注意的是,这种方法通过对对象方法重写来实现对原有方法扩展目的,但是有时候会导致this指向丢失问题。比如下面这个例子:
var _getElementById = document.getElementById;document.getElementById = function( id ){alert (1);return _getElementById( id ); // (1)}var button = document.getElementById( 'button' );
执行这个方法,会在弹出1后报错,原因在于返回的_getElementById(id)该函数处于全局环境中,也就意味者在这里面this指向了window,然而跟据定义_getElementById(id)应该指向的是document这个对象。
完美实现装饰者模式
前面我们简单的将对象赋值给一个对象保存,然后再改变这个对象,但是this引用却没有保存下来,就导致this丢失问题。所以我们就需要解决this丢失问题。
var before = function(fn,beforefn){return function(){beforefn.apply(this,arguments);return fn.apply(this,arguments);}}var after = function(fn,afterfn){return function(){const ret = fn.apply(this,arguments);afterfn.apply(this,arguments);return ret}}//使用案例let a = before(document.getElementById( 'button' ),function(){alert (1);})a.call(document,"ReactApp")
在这里主要作用是需要保存this指向,也就是说在调用这个函数的时候需要非常明确知道this到底指向那里,幸运的是我们也有一种方法不需要知道this指向哪里也就是将before和after方法添加到function原型链上,这个希望你们自己能够去实现。
