资讯专栏INFORMATION COLUMN

聊聊Typescript中的设计模式——装饰器篇(decorators)

yiliang / 840人阅读

摘要:本文从装饰模式出发,聊聊中的装饰器和注解。该函数的函数名。不提供元数据的支持。中的元数据操作可以通过包来实现对于元数据的操作。

  随着Typescript的普及,在KOA2和nestjs等nodejs框架中经常看到类似于java spring中注解的写法。本文从装饰模式出发,聊聊Typescipt中的装饰器和注解。

什么是装饰者模式

Typescript中的装饰器

Typescript中的注解

总结

原文地址:https://github.com/forthealll...

欢迎star

一、什么是装饰者模式

  最近在看nestjs等支持Typescript的node框架,经常看到这样一种写法:

import { Controller, Get } from "@nestjs/common";

@Controller("cats")

export class CatsController {
  @Get()
  findAll() {
    return "This action returns all cats";
  }
}

  上述代码定义了一个处理url为“/cats”的控制器,该控制器对于url为“/cats”的get方法执行findAll()函数,返回相应的字符串。

  在上述的代码中,用@Controller("cats")修饰CatsController类,通过@Get来修饰类中的findAll方法,这就是典型的装饰者模式。通过@Controller("cats")和@Get修饰后的类CatsController,简单来说,就是拥有了丰富的“内涵”。

下面看看具体装饰者模式的定义:

我们知道继承模式是丰富子元素“内涵”的一种重要方式,不管是继承接口还是子类继承基类。而装饰者模式可以在不改变继承关系的前提下,包装先有的模块,使其内涵更加丰富,并不会影响到原来的功能。与继承相比,更加的灵活。

javascript中的装饰器处于建议征集的第二阶段,通过babel和Typescrit都可以实现装饰器的语法。

二、Typescript中的装饰器

Typescript中的装饰器与类相关,分别可以修饰类的实例函数和静态函数、类本身、类的属性、类中函数的参数以及类的set/get存取器,下面来意义介绍。

(1)、类方法的装饰器

下面来介绍一下用装饰器来修饰函数,首先来看一个例子:

let temple;
function log(target, key, descriptor) {
  console.log(`${key} was called!`);
  temple = target;
}
class P {
    @log
    foo() {
      console.log("Do something");
    }
}

const p = new P()
p.foo()
console.log(P.prototype === temple) //true

上述是实例方法foo中我们用log函数修饰,log函数接受三个参数,通过P.prototype === temple(target)可以判断,在类的实例函数的装饰器函数第一个参数为类的原型,第二个参数为函数名本身,第三个参数为该函数的描述属性。

具体总结如下,对于类的函数的装饰器函数,依次接受的参数为:

target:如果修饰的是类的实例函数,那么target就是类的原型。如果修饰的是类的静态函数,那么target就是类本身。

key: 该函数的函数名。

descriptor:该函数的描述属性,比如 configurable、value、enumerable等。

从上述的例子中我们可以看到,用装饰器来修饰相应的类的函数十分方便:

@log
foo() {
  ...
}
(2)、类的装饰器

装饰函数也可以直接修饰类:

let temple
function foo(target){
   console.log(target);
   temple = target
}
@foo
class P{
   constructor(){
     
   }
}

const p = new P();
temple === P //true

当装饰函数直接修饰类的时候,装饰函数接受唯一的参数,这个参数就是该被修饰类本身。上述的例子中,输出的target就是类P的本身。

此外,在修饰类的时候,如果装饰函数有返回值,该返回值会重新定义这个类,也就是说当装饰函数有返回值时,其实是生成了一个新类,该新类通过返回值来定义。

举例来说:

function foo(target){
   return class extends target{
      name = "Jony";
      sayHello(){
         console.log("Hello "+ this.name)
      }
   }
}
@foo
class P{
   constructor(){
     
   }
}

const p = new P();
p.sayHello(); // 会输出Hello Jony

上面的例子可以看到,当装饰函数foo有返回值时,实际上P类已经被返回值所代表的新类所代替,因此P的实例p拥有sayHello方法。

(3)、类的属性的装饰器

下面我们来看类的属性的装饰器,装饰函数修饰类的属性时,在类实例化的时候调用属性的装饰函数,举例来说:

function foo(target,name){
   console.log("target is",target);
   console.log("name is",name)
}
class P{
   @foo
   name = "Jony"
}
const p = new P();
//会依次输出 target is f P()  name is Jony

这里对于类的属性的装饰器函数接受两个参数,对于静态属性而言,第一个参数是类本身,对于实例属性而言,第一个参数是类的原型,第二个参数是指属性的名字。

(4)、类函数参数的装饰器

接着来看类函数参数的装饰器,类函数的参数装饰器可以修饰类的构建函数中的参数,以及类中其他普通函数中的参数。该装饰器在类的方法被调用的时候执行,下面来看实例:

function foo(target,key,index){
   console.log("target is",target);
   console.log("key is",key);
   console.log("index is",index)
}
class P{
   test(@foo a){
   }
}
const p = new P();
p.test("Hello Jony")
// 依次输出 f P() , test , 0 

类函数参数的装饰器函数接受三个参数,依次为类本身,类中该被修饰的函数本身,以及被修饰的参数在参数列表中的索引值。上述的例子中,会依次输出 f P() 、test和0。再次明确一下修饰函数参数的装饰器函数中的参数含义:

target: 类本身

key:该参数所在的函数的函数名

index: 该参数在函数参数列表中的索引值

从上面的Typescrit中在基类中常用的装饰器后,我们发现:

装饰器可以起到分离复杂逻辑的功能,且使用上极其简单方便。与继承相比,也更加灵活,可以从装饰类,到装饰类函数的参数,可以说武装到了“牙齿”。

三、Typescript中的注解

在了解了Typescrit中的装饰器之后,接着我们来看Typescrit中的注解。

什么是注解,所谓注解的定义就是:

为相应的类附加元数据支持

所谓元数据可以简单的解释,就是修饰数据的数据,比如一个人有name,age等数据属性,那么name和age这些字段就是为了修饰数据的数据,可以简单的称为元数据。

元数据简单来说就是可以修饰某些数据的字段。下面给出装饰器和注解的解释和区别:

装饰器:定义劫持,可以对类,类的方法,类的属性以及类的方法的入参进行修改。不提供元数据的支持。

注解:仅提供元数据的支持。

两者之间的联系:

通过注解添加元数据,然后在装饰器中获取这些元数据,完成对类、类的方法等等的修改,可以在装饰器中添加元数据的支持,比如可以可以在装饰器工厂函数以及装饰器函数中添加元数据支持等

(1)、Typescript中的元数据操作

可以通过reflect-metadata包来实现对于元数据的操作。首先我们来看reflect-metadata的使用,首先定义使用元数据的函数:

const formatMetadataKey = Symbol("format");

function format(formatString: string) {
    return Reflect.metadata(formatMetadataKey, formatString);
}

function getFormat(target: any, propertyKey: string) {
    return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

这里的format可以作为装饰器函数的工厂函数,因为format函数返回的是一个装饰器函数,上述的方法定义了元数据Sysmbol("format"),用Sysmbol的原因是为了防止元数据中的字段重复,而format定义了取元数据中相应字段的功能。

接着我们来在类中使用相应的元数据:

class Greeter {
    @format("Hello, %s")
    name: string;

    constructor(name: string) {
        this.name = message;
    }
    sayHello() {
        let formatString = getFormat(this, "name");
        return formatString.replace("%s", this.name);
    }
}

const g = new Greeter("Jony");
console.log(g.sayHello());

在上述中,我们在name属性的装饰器工厂函数,执行@format("Hello, %s"),返回一个装饰器函数,且该装饰器函数修饰了Greeter类的name属性,将“name”属性的值写入为"Hello, %s"。

然后再sayHello方法中,通过getFormat(this,"name")取到formatString为“Hello,%s”.

四、总结

通过装饰器,可以方便的修饰类,以及类的方法,类的属性等,相比于继承而言更加灵活,此外,通过注解的方法,可以在Typescript中引入元数据,实现元编程等。特别是在angularjs、nestjs中,大量使用了注解,特别是nestjs构建了类似于java springMVC式的web框架。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100093.html

相关文章

  • TS下的装饰模式(Decorator)

    摘要:装饰者模式装饰者模式就是动态的给类或对象增加功能的设计模式。下的实现里的装饰器目前处在建议征集的第二阶段,不被浏览器所支持,如果想要提前使用这个新特性就需要,等工具进行转译。这里介绍下的用法。 1.1、装饰者模式 装饰者模式就是动态的给类或对象增加功能的设计模式。在程序运行时动态的给一个具备基础功能的类或对象添加新的功能,并且不会改变会破坏基础类和对象的功能。先提炼出产品的最小可用产品...

    SolomonXie 评论0 收藏0
  • 装饰器与元数据反射(1)方法装饰

    摘要:使用装饰器的方法很简单在装饰器名前加字符,写在想要装饰的方法上,类似写注释的方式装饰器实际上是一个函数,入参为所装饰的方法,返回值为装饰后的方法。经过装饰过的方法,它依然按照原来的方式执行,只是额外执行了附件的装饰器函数的功能。 让我来深入地了解一下TypeScript对于装饰器模式的实现,以及反射与依赖注入等相关特性。 在Typescript的源代码中,可以看到装饰器能用来修饰cla...

    xiaochao 评论0 收藏0
  • vue + typescript 项目起手式

    摘要:新项目起手式最后更新于,技术文具有时效性,请知悉我知道你们早就想用上强类型了还有后续进阶篇安装安装依赖配置添加添加让识别改造文件什么是是的强类型版本。是的超集,这意味着他支持所有的语法。与此同时,也是的超集,的也宣布采用进行开发。 vue + typescript 新项目起手式 最后更新于2018-06-30,技术文具有时效性,请知悉 我知道你们早就想用上 vue + ts 强类型...

    zhisheng 评论0 收藏0
  • ES6 系列之我们来聊聊装饰

    摘要:第二部分源码解析接下是应用多个第二部分对于一个方法应用了多个,比如会编译为在第二部分的源码中,执行了和操作,由此我们也可以发现,如果同一个方法有多个装饰器,会由内向外执行。有了装饰器,就可以改写上面的代码。 Decorator 装饰器主要用于: 装饰类 装饰方法或属性 装饰类 @annotation class MyClass { } function annotation(ta...

    eternalshallow 评论0 收藏0
  • JS 装饰器解析

    摘要:在的中,定义了种不同装饰器的接口,其中装饰类以及装饰类方法的接口定义如下所示下面对这两种情况进行解析。作用于类的装饰器当装饰的对象是类时,我们操作的就是这个类本身。相关链接中的装饰器装饰器场景实战修饰器 showImg(https://segmentfault.com/img/remote/1460000013825346?w=640&h=280); 随着 ES6 和 TypeScri...

    zzbo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<