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

js装饰器介绍(ts装饰器介绍)

时间:2023-03-27 18:07:51 JavaScript

参考js装饰器@Decorator装饰器-阮一峰TS装饰器(二):MetadataTS文档-装饰器提示TypeScript已经完全实现了装饰器,js装饰器是语法还是提案中。如果你使用js而不是ts,你需要配置Babel才能使用阅读本文之前需要了解的前置知识:js类、Object.defineProperty、js原型、闭包文章介绍装饰器是基于ts实现的。常用装饰器示例先不说装饰器是如何实现的,先来看两个装饰器示例,从而对装饰器有一个初步的认识示例一:readonlyclassPerson{constructor(name){this.name=name}getName(){returnthis.name}}上面代码定义了一个类Person,它有一个原型方法getName,显然getName可以修改Person.prototype.getName=function(){return`hahaha`}console.log(newPerson('测试').getName());//哈哈哈如果我现在要求getName不被修改,我可以用一个装饰器来实现效果//这个是只读的,具体实现可以先忽略,后面会详细介绍。functionreadonly(target,name,descriptor){descriptor.writable=false;}classPerson{constructor(name){this.name=name;}@readonlygetName(){返回this.name;}}//下面的语句会报错,提示getNamereadonlyPerson.prototype.getName=function(){return`hahaha`;};console.log(newPerson('test').getName());在上面的代码中,我们在getName方法的定义中添加了一行@readonly,装饰器就是这样写的。它非常直观,就像评论一样。修改示例2:deprecate当我们调用第三方库的方法时,经常会在控制台看到一些警告提示这个方法即将被移除。这种效果可以通过使用装饰器import{deprecate}from'core-decorators'来实现;//是一个第三方模块,提供了几个常用的装饰器http://knowyourmeme.com/memes/facepalm'})facepalmHarder(){}}constperson=newPerson();person.facepalm();//弃用Person#facepalm:此功能将在未来版本中删除。person.facepalmHard();//弃用Person#facepalmHard:我们停止了facepalmingperson.facepalmHarder();//弃用Person#facepalmHarder:我们停止了facepalming////请参阅http://knowyourmeme.com/memes/facepalm了解更多详情.//当我们调用deprecate装饰器装饰的facepalm方法时,控制台会显示警告,提示该方法将被废除。可以传入不同的参数deprecate来控制显示内容。通过上面两个例子,我们可以看出装饰器优雅地改变了类的原有行为。它是可重用的,并且直观地表达了装饰器是什么。装饰器本质上是一个函数,写成@+函数名,可以对类进行添加或修改。函数装饰器的一个使用场景是将通用的需求函数从不相关的类中分离出来,让多个类可以共享一个行为。一旦发生变化,不需要修改很多类,只需要修改这个行为比如上面例子中的readonly和deprecate,它们的功能可能会被很多类使用,但是与类本身无关,所以很适合用装饰器来实现装饰器的使用。首先要明确的是,装饰器不能在代码的任何地方使用,它只能用于类,可以放在类定义、类属性、类方法、访问器、参数(包括类构造函数和方法)前面类装饰器(放在类定义前面)基本上,装饰器的行为如下@decoratorclassA{}//相当于classA{}A=decorator(A)||A;也就是说,装饰器是处理类的函数。装饰器函数的第一个参数是要装饰的目标类。示例3:使用装饰器为类添加静态属性//定义一个装饰器函数constaddPropertyA=(target)=>{//这里的目标是类本身(即ClassA)target.a='a';//给类(ClassA)添加一个静态属性};@addPropertyAclassClassA{constructor(){this.a=1;}}console.info('ClassA.a:',ClassA.a);//aconsole.info('a:',newClassA().a);//1tips:可以使用在线编译ts将上面的ts代码编译成js(es2017)帮助理解示例4:使用装饰器为类添加实例属性本示例为类ClassA添加原型方法constaddMethodTest=(target)=>{target.prototype.test=()=>{console.log('test');};};@addMethodTestclassClassA{}newClassA().test();//测试示例5:在类装饰器传参示例3中,我们给ClassA添加一个静态属性a,其值为字符串'a'(硬编码字面量),如果属性a的值需要作为参数传入?这时候需要在装饰器外再封装一层函数functionaddPropertyA(value){//这是一个装饰器工厂返回函数(target){//这是装饰器target.a=value;};}//箭头函数写法//constaddPropertyA=(value)=>(target)=>{//target.a=value;//};@addPropertyA('test')classClassA{}console.info('ClassA.a:',ClassA.a);//上面的测试等价于functiongetAddPropertyA(value){returnfunction(target){target.a=值;};}constaddPropertyA=getAddPropertyA('test');@addPropertyAclassClassA{}console.info('ClassA.a:',ClassA.a);//测试方法装饰器(放在class方法定义的前面)例子6:readonly看文章开头的第一个例子,我们有一个Person类classPerson{constructor(name){this.name=name}getName(){returnthis.name}}如果我问现在getName不能修改,可以用Object.defineProperty定义getNameclassPerson{constructor(name:string){this.name=name;}}Object.defineProperty(Person.prototype,'getName',{writable:false,value(){returnthis.name;},});//下面语句会报错,提示getNamereadonlyPerson.prototype.getName=function(){return`hahaha`;};console.log(newPerson('test').getName());functionreadonly(target,name,descriptor){descriptor.writable=false;}classPerson{构造函数(名称){this.name=名称;}@readonlygetName(){返回this.name;}}//下面的语句会报错,提示getNamereadonlyPerson.prototype.getName=function(){return`hahaha`;};console.log(newPerson('test').getName());写上面两个该方法达到了同样的效果,但第二种显然更优雅。readonly是一个装饰函数。第一个参数是类的原型对象。上面的例子是Person.prototype(注意不同于作用于类的装饰器),第二个第一个参数是要装饰的属性名(上面的例子是getName),第三个参数是属性的描述对象(与Object.defineProperty中传入的描述对象相同)。上面的代码表明装饰器(readonly)可以修改属性描述对象(descriptor),然后使用修改后的描述对象来定义属性;//是一个第三方模块,提供了几个常用的装饰器classPerson{@readonlyname='Jack';}constperson=newPerson();person.name='Rose';//TypeError:Cannotassigntoreadonlyproperty'name'ofobject'#'其他参数装饰器本文不介绍