当前位置: 首页 > Web前端 > HTML5

ES7-Decorator-装饰器模式

时间:2023-04-05 13:52:56 HTML5

装饰模式只是对已有的模块进行包装,使其“更华丽”,不会影响原有界面的功能——就像给手机加壳一样,不影响手机原有的通话、充电等功能;usingES7'sdecoratorES7中增加了一个decorator属性,这个属性是从Python借来的。下面以钢铁侠为例,讲解一下如何使用ES7的装饰器。以钢铁侠为例。钢铁侠本质上是一个人,只是在“装饰”了很多武器之后才变得如此NB,但无论怎么装饰,他仍然是一个人。我们的示例场景是这样的:先创建一个普通人类,抗性为2,攻击力为3,血量为3;然后我们让它穿上钢铁侠的盔甲,这样他的抗性增加100,变成102;让他戴上光束手套,攻击力增加50,变成53;最后让他增加“飞行”能力【Demo1】装饰方法:装备盔甲创建Man类:classMan{constructor(def=2,atk=3,hp=3){this.init(def,攻击力,生命值);}init(def,atk,hp){this.def=def;//防御值this.atk=atk;//攻击力this.hp=hp;//血量}toString(){返回`防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;}}vartony=newMan();console.log(`currentstate===>${tony}`);//output:currentstate===>defense:2,attack:3,HP:3代码直接放在http://babeljs.io/repl/并运行它以查看结果。记得勾选Setting的Evaluate选项,还有options的选项tocreateadecorateArmourmethodforlegacytoassemblearmorforIronMan——注意decorateArmour是方法init上的装饰。functiondecorateArmour(target,key,descriptor){constmethod=descriptor.value;让moreDef=100;让我们休息;descriptor.value=(...args)=>{args[0]+=moreDef;ret=方法。申请(目标,参数);返还;}returndescriptor;}classMan{constructor(def=2,atk=3,hp=3){this.init(def,atk,hp);}@decorateArmourinit(def,atk,hp){this.def=def;//防御值this.atk=atk;//攻击力this.hp=hp;//血量}toString(){return`防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;}}vartony=newMan();console.log(`currentstate===>${tony}`);//Output:Currentstate===>Defense:102,Attack:3,HP:3先看输出,防御确实有增加了100,看来盔甲起作用了。Decorators的本质是利用了ES5的Object.defineProperty属性。这三个参数其实和Object.defineProperty参数是一致的。【Demo2】Decorator叠加:添加“光束手套铠甲”装饰;现在想给他添加“光束手套”,希望能增加50点防御值。...functiondecorateLight(target,key,descriptor){constmethod=descriptor.value;让moreAtk=50;让我们休息;descriptor.value=(...args)=>{args[1]+=moreAtk;ret=method.apply(target,args);返还;}returndescriptor;}classMan{constructor(def=2,atk=3,hp=3){this.init(def,atk,hp);}@decorateArmour@decorateLightinit(def,atk,hp){this.def=def;//防御值this.atk=atk;//攻击力this.hp=hp;//血量}...}vartony=newMan();console.log(`currentstate===>${tony}`);//输出:currentstate===>防御力:102,攻击力:53,血量:3在这里你可以看到装饰模式的优点。可以叠加在某个方法上,对原类的侵入很小,只需要加一行@decorateLight即可,可以方便的增删;(并可同时重复使用)【Demo3】班级装饰:增加飞行能力。有两种装饰模式:纯装饰模式和半透明装饰模式。上面两个demo应该是纯装饰模式,没有给原来的类增加接口;下面的demo是给普通人添加“飞行”的能力,相当于给类添加了一个方法。属于半透明装饰模式,有点像适配器模式。...//3functionaddFly(canFly){returnfunction(target){target.canFly=canFly;让额外的=canFly?'(技能加成:飞行能力)':'';让方法=target.prototype.toString;target.prototype.toString=(...args)=>{returnmethod.apply(target.prototype,args)+extra;}返回目标;}}@addFly(true)classMan{构造函数(def=2,atk=3,hp=3){this.init(def,atk,hp);}@decorateArmour@decorateLightinit(def,atk,hp){this.def=def;//防御值this.atk=atk;//攻击力this.hp=hp;//血量}...}...console.log(`currentstate===>${tony}`);//output:currentstate===>Defense:102,Attack:53,HP:3(技能加成:飞行能力)作用于方法的装饰器接收到的第一个参数(target)是类的原型;如果装饰器应用于类,它的第一个参数目标是类本身。使用原生JS实现装饰器模式。Man是具体类,Decorator是Man的装饰器基类。具体的装饰类DecorateArmour一般是通过原型继承继承自Decorator基类;基于IOC(inversionofcontrol)的思想,Decorator是AcceptMan类,而不是自己创建Man类;//首先我们必须创建一个基类函数Man(){this.def=2;这个.atk=3;this.hp=3;}//装饰器也需要实现这些方法,按照Man的接口Man.prototype={toString:function(){return`defensepower:${this.def},attackpower:${this.atk},血量:${this.hp}`;}}//创建一个接收Man对象作为参数的装饰器。varDecorator=function(man){this.man=man;}//装饰器应该实现这些相同的方法Decorator.prototype.toString=function(){returnthis.man.toString();}//继承自decorationDecoratorobject//创建一个特定的装饰器,它也接收Man对应的参数varDecorateArmour=function(man){v??armoreDef=100;man.def+=moreDef;Decorator.call(this,man);}DecorateArmour.prototype=newDecorator();//接下来我们需要为每个函数创建一个装饰器对象,重写父方法,添加我们想要的函数。DecorateArmour.prototype.toString=function(){returnthis.man.toString();}//注意这里的调用方法//构造函数相当于一个“过滤器”,vartonyforaspect-oriented=newMan();tony=newDecorateArmour(tony);console.log(`currentstate===>${tony}`);//输出:当前状态===>防御力:102,攻击力:3,血量:3经典实现:Logger的经典应用是日志系统,所以我们也使用ES7语法为钢铁侠创建一个日志系统。/***由jscon于2016年15月10日创建。*/letlog=(type)=>{return(target,name,descriptor)=>{constmethod=descriptor.value;descriptor.value=(...args)=>{console.info(`(${type})正在执行:${name}(${args})=?`);让我们休息;尝试{ret=method.apply(target,args);console.info(`(${type})成功:${name}(${args})=>${ret}`);}catch(error){console.error(`(${type})失败:${name}(${args})=>${error}`);返回ret;}}}classIronMan{@log('IronMan自测阶段')check(){return'checkcompleted';}@log('钢铁侠攻击阶段')attack(){return'击倒敌人';}@log('IronManbodyreportedanerror')error(){throw'出事了!';}}vartony=newIronMan();tony.check();tony.attack();tony.error();//输出://(IronMan自检阶段)执行:check()=?//(IronMan自检阶段)Success:check()=>Checkcompleted//(IronMan攻击阶段)Executing:attack()=?//(IronMan攻击阶段)Success:attack()=>击倒敌人//(IronManbody报错)Executing:error()=?//(IronManbodyerror)failed:error()=>出错了!Logger方法的关键在于:首先使用constmethod=descriptor.value;对原方法进行提取,保证原方法的纯度;在try..catch语句中是调用ret=method.apply(target,args);记录通话前后的报告;最后返回返回ret;原来调用结果相关库https://github.com/jayphelps/...vue使用装饰在JavaScript中实现AOP编程在JavaScript中实现AOP就是将一个函数“动态编织”成另一个函数首先要构造函数的原型//prototype.jsFunction.prototype.before=function(beforefn){let_self=this;返回函数(){beforefn.apply(this,arguments);返回_self.apply(this,arguments);};};Function.prototype.after=function(afterfn){let_self=this;返回函数(){letret=_self.apply(this,arguments);afterfn.apply(this,arguments);返还;};};Function.prototype.around=function(beforefn,afterfn){let_self=this;返回函数(){beforefn.apply(this,arguments);让ret=_self.apply(this,arguments);afterfn.apply(this,arguments);返还;};};编辑我们的装饰器函数//decorator.jsexportconstbefore=function(...args){returnfunction(target,key,descriptor){descriptor.value=descriptor.value.before(()=>{console.log(`Action-${key}触发埋点!`);});};};exportconstafter=function(...args){returnfunction(target,key,descriptor){descriptor.value=descriptor.value.after(()=>{console.log(`Action-${key}触发埋点!`);});};};exportconstaround=function(...args){returnfunction(target,key,descriptor){descriptor.value=descriptor.value.around(()=>{console.log(`Action-${key}之前触发埋点!`);},()=>{console.log(`Action-${key}之后触发埋点!`);});};};编辑我们的vue文件//test.vue.babelrcfile{"plugins":[["@babel/plugin-proposal-decorators",{"legacy":true}]]}来自http://www.liuweibo.cn/p/254#...淘宝前端团队https://www.jianshu.com/p/208...今天的图丢人了