从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
从java注解漫谈到typescript装饰器——注解与装饰器的更多相关文章
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...
- typescript装饰器 方法装饰器 方法参数装饰器 装饰器的执行顺序
/* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...
- typescript装饰器定义 类装饰器 属性装饰器 装饰器工厂
/* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...
- 十八、泛型 l 注解 l Servlet3.0 l 动态代理 l 类加载器基础加强
l 泛型 l 注解 l Servlet3.0 l 动态代理 l 类加载器 泛型 1 回顾泛型类 泛型类:具有一个或多个泛型变量的类被称之为泛型类. public class A<T> { ...
- Struts2:效验器——注解
效验器三类: 编程式——Java代码 声明式——xml 注释法——@ 注解验证可以修饰属性的getter方法,也可以修饰执行方法Action中校验失败时,返回input逻辑视图 struts.xml ...
- Java基础笔记 – Annotation注解的介绍和使用 自定义注解
Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 | 被围观 25,969 views+ 1.Anno ...
- [置顶] 使用struts拦截器+注解实现网络安全要求中的日志审计功能
J2EE项目中出于安全的角度考虑,用户行为审计日志功能必不可少,通过本demo可以实现如下功能: 1.项目中记录审计日志的方法. 2.struts拦截器的基本配置和使用方法. 3.struts拦截器中 ...
- Java注解处理器--编译时处理的注解
1. 一些基本概念 在开始之前,我们需要声明一件重要的事情是:我们不是在讨论在运行时通过反射机制运行处理的注解,而是在讨论在编译时处理的注解.注解处理器是 javac 自带的一个工具,用来在编译时期扫 ...
- spring mvc:内部资源视图解析器(注解实现)@Controller/@RequestMapping
spring mvc:内部资源视图解析器(注解实现)@Controller/@RequestMapping 项目访问地址: http://localhost:8080/guga2/hello/prin ...
- 《Java编程思想》笔记 第二十章 注解
1.注解 注解也称元数据,是在代码中添加信息的一种方式添加的信息提供给编译器或者工具类框架使用. SE5引入,可以提供用来完整描述程序所需要的信息,往往这些信息是无法用Java来表达的. 注解可以在编 ...
随机推荐
- JavaScript 语法:变量、数据类型及数据类型转换
作者:WangMin 格言:努力做好自己喜欢的每一件事 变量 赋值变量用 var 关键字,情况如下: 1)先声明变量再赋值 var varName; varName="你好~"; ...
- Acwing127周赛第三题 构造矩阵 (套路)
题目链接:构造矩阵 题目描述 我们希望构造一个 n×m 的整数矩阵. 构造出的矩阵需满足: 每一行上的所有元素之积均等于 k. 每一列上的所有元素之积均等于 k. 保证 k 为 1 或 −1. 请你计 ...
- 开发现代化的.NetCore控制台程序:(4)使用GithubAction自动构建以及发布nuget包
前言 上一篇文章介绍了将 nuget 包发布到 Github Packages 上. 本文更进一步,使用 GitHub Action 搭建 CI/CD 流水线,进行 nuget 的自动构建和发布. G ...
- 神经网络入门篇:详解向量化实现的解释(Justification for vectorized implementation)
向量化实现的解释 先对几个样本计算一下前向传播,看看有什么规律: 公式1.16: \(z^{[1](1)} = W^{[1]}x^{(1)} + b^{[1]}\) \(z^{[1](2)} = W^ ...
- JavaScript高级程序设计笔记01 什么是JavaScript
什么是JavaScript 1995年问世. 最初在客户端处理某些基本的验证. 名字:Mocha -> LiveScript -> JavaScript ECMAScript脚本语言标准: ...
- [CTF/Web] PHP 反序列化学习笔记
Serialize & unserialize 这两个方法为 PHP 中的方法, 参见 serialize 和 unserialize 的官方文档. 以下内容中可能存在 字段, 属性, 成员 ...
- 品牌全渠道营销系统如何与不同经销商ERP打通
品牌商在与各经销商ERP系统打通方面面临的挑战.传统的ERP系统往往使得数据收集和合作变得繁琐且低效,导致市场响应迟缓,影响整体的供应链管理和市场决策.我们的解决方案旨在破解这一难题,提供一个全渠道营 ...
- JavaWeb-JS基础
4.JS基础 (1)JS的引入方式 HTML内部引入 将JS代码放在"< script >< /script >"标签之间 在HTML文档中,可以在任意地方 ...
- ASP.NET Core 8 在 Windows 上各种部署模型的性能测试
ASP.NET Core 8 在 Windows 上各种部署模型的性能测试 我们知道 Asp.net Core 在 windows 服务器上部署的方案有 4 种之多.这些部署方案对性能的影响一直以来都 ...
- [python][图像切割]给定手写数字图片完成数字切割
import torch import torch.nn as nn from torchvision import transforms from PIL import Image, ImageOp ...