从java注解漫谈到typescript装饰器——注解与装饰器

之前整理过《Java注解(批注)的基本原理》,在java里面,,注解(Annotation)是油盐,对于JavaScript来说,还中世纪欧洲的东方香料

装饰器和注解

装饰器和注解之前也搞不清他们的具体理念,觉得都是基于元编程实现,注解就是装饰模式的一种吧。

注解(Annotation):仅提供附加元数据支持,并不能实现任何操作。需要另外的 Scanner 根据元数据执行相应操作。

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

注解与装饰器两者之间的联系:

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

注解与装饰器的区别

虽然语法上很相似,但在不同的语言中可能使用的是不同的概念:

使用注解(Annotation)的语言:AtScript、Java、C#(叫 Attribute)。

使用装饰器(Decorator)的语言:Python、JavaScript/ECMAScript。

从概念上来说,我们可以很清晰的看出,注解和装饰器在语义上没有任何共性!

注解和装饰器可以互相模拟,不等同。 装饰器可以天生跑在运行时,注解还要通过反射(拿不到类型本身)

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

装饰器最为强大的功能之一是它能够反射元数据(reflect metada)

为什么需要在JavaScript中进行反射?

反射用于描述能够检查同一系统(或其自身)中的其他代码的代码。

JavaScript应用程序越来越大,所以需要一些工具(如控件容器的反转)和像(运行时类型断言)这样的功能来管理这种日益增加的复杂性。

强大的反射API应该允许我们在运行时检查未知对象并找出有关它的所有内容。我们应该能够找到像这样的东西:

实体的名称。

实体的类型。

哪些接口由实体实现。

实体属性的名称和类型。

实体的构造函数参数的名称和类型

在JavaScript中,我们可以使用Object.getOwnPropertyDescriptor()或Object.keys()等函数来查找有关实体的一些信息,但我们需要反思来实现更强大的开发工具。

但是,事情即将发生变化,因为TypeScript开始支持一些Reflection功能。但实际上它们只是一些 JavaScript 函数,能够帮助我们来注释代码或者是修改代码的行为——这种做法我们通常称为元编程。

TypeScript 装饰器

装饰器能够很好的抽象代码,它们最适合用来包装可能会多处复用的逻辑。

五种装饰器的方法

类声明

属性

方法

参数

accessor

类装饰器 Class Decorator

类装饰器使得开发者能够拦截类的构造方法 constructor

注意:当我们声明一个类时,装饰器就会被调用,而不是等到类实例化的时候。

当你装饰一个类的时候,装饰器并不会对该类的子类生效,让我们来冻结一个类来彻底避免别的程序员不小心忘了这个特性。

@Frozen

class IceCream {}

function Frozen(constructor: Function) {

  Object.freeze(constructor);

  Object.freeze(constructor.prototype);

}

console.log(Object.isFrozen(IceCream)); // true

class FroYo extends IceCream {} // 报错,类不能被扩展

当装饰函数直接修饰类的时候,装饰函数接受唯一的参数constructor,这个参数就是该被修饰类本身。

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

方法装饰器 Method Decorator

方法装饰器来覆写一个方法,改变它的执行流程,以及在它执行前后额外运行一些代码

下面这个例子会在执行真正的代码之前弹出一个确认框。如果用户点击了取消,方法就会被跳过。注意,这里我们装饰了一个方法两次,这两个装饰器会从上到下地执行。

function log(target, key, descriptor) {}

class P {

    @log

    foo() {

      console.log('Do something');

    }

}

对于类的函数的装饰器函数,依次接受的参数为:

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

key: 该函数的函数名。

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

属性装饰器 Property Decorator

属性装饰器极其有用,因为它可以监听对象状态的变化

为了充分了解接下来这个例子,建议你先熟悉一下 JavaScript 的属性描述符(PropertyDescriptor)。

function foo(target,name){}

class P{

   @foo

   name = 'Jony'

}

这里对于类的属性的装饰器函数接受两个参数,

第一个参数:

对于静态属性而言,是类本身

对于实例属性而言,是类的原型,

第二个参数:所指属性的名字。

类函数参数的装饰器

类函数的参数装饰器可以修饰类的构建函数中的参数,以及类中其他普通函数中的参数。该装饰器在类的方法被调用的时候执行

function foo(target,key,index){}

class P{

   test(@foo a){

   }

}

类函数参数的装饰器函数接受三个参数

target: 类本身

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

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

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

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中的装饰器&元数据反射:从新手到专家四 https://zhuanlan.zhihu.com/p/42220487

理解 TypeScript 装饰器 https://zhuanlan.zhihu.com/p/65764702

【认真脸】注解与装饰器的点点滴滴https://zhuanlan.zhihu.com/p/22277764

聊聊Typescript中的设计模式——装饰器篇(decorators) https://github.com/forthealllight/blog/issues/33

转载本站文章《从java注解漫谈到typescript装饰器——注解与装饰器》,

请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/typescript/2020_0721_8528.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 先看一段代码 这是一段TypeScript上 koa 路由类的写法,注意到在其中,使用了@Paht @Get的写法...
    Mr_Treasure阅读 17,647评论 9 16
  • 参数装饰器 参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰...
    2o壹9阅读 840评论 1 49
  • 大纲 本章主要讲解一些ts的高级用法,涉及以下内容: 类型断言与类型守卫 in关键词和is关键词 类型结构 装饰器...
    aliensq阅读 467评论 0 1
  • 访问器装饰器 访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符...
    2o壹9阅读 1,030评论 1 49
  • 回顾类的属性 回顾面向对象,类中的属性分为实例属性和静态属性,实例属性属于实例化后的对象,静态属性属于类本身 在类...
    Rollover阅读 2,251评论 0 0