TypeScript学习笔记(四)装饰器
一、装饰器的作用
我个人的理解是:ts
中的装饰器类似于 Java
语言中的注解,对于用户来说都是为类和属性等代码元素
添加额外的功能,而不改变代码元素
原有的结构。例如在 Java
中我们用的比较多的 Spring
框架中的注解 @Component
可以将一个类放置到 IoC
容器中进行托管,使用 @Autowired
来取出该对象进行使用等等。
二、类装饰器
1. 普通装饰器
先写一个最简单的类装饰器,它看起来就是一个方法:
function logClass(params:any) {
console.log(params);
}
使用方式如下,非常类似Java
的注解:
@装饰器名
class 被装饰的类 {
...
}
然后我们编写一个类来进行装饰:
@logClass // <-- 类装饰器的使用方式
class HttpClient {
constructor() {
}
getData() {
}
}
查看运行的结果:
可以看出,logClass
参数 params
中得到的是修饰的类(在console
中打印出类型是方法的原因是ts
中的类在编译成js
代码后,会变成一个函数)
为类扩展属性和方法
扩展属性
这里为被修饰的类添加一个 info
属性:
function logClass(params:any) {
// 在原型链上添加附加属性
params.prototype.info = '附加信息'
}
然后我们将这个属性打印出来:
let httpClient:any = new HttpClient();
console.log(httpClient.info);
运行结果:
tips:注意这里的创建的对象的类型需要指定为
any
才可以使用info
属性
扩展方法
同理,我们也可以为被修饰的类扩展方法:
function logClass(params:any) {
params.prototype.run = function() {
console.log("我是扩展的run方法");
}
}
方法调用:
let httpClient:any = new HttpClient();
httpClient.run();
运行结果:
使用装饰器修改属性和重写方法
语法:
function 装饰器名(target:any) {
return class extends target {
原有的某属性:any = "我是修改后的数据";
原有的某方法() {
//...重写的内容
}
}
}
示例:
function logClass(target:any) {
return class extends target {
apiUrl:any = "我是修改后的数据";
getData() {
this.apiUrl += "-----";
console.log(this.apiUrl);
}
}
}
被修饰的类:
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
}
getData() {
}
}
运行代码:
let httpClient:HttpClient = new HttpClient();
httpClient.getData();
运行结果:
可以看到,使用装饰器,我们不需要编写子类即可修改类的属性和重写类的方法了!
2. 装饰器工厂
当我们使用普通装饰器时可以发现,我们是无法传递参数的,而现在要学的装饰器工厂则可以传递参数。
语法格式:
function 装饰器名(参数列表) {
return function(target: any) {
...
}
}
现在参数列表
中的是我们输入的参数,而 target
是被装饰的类。
示例:
function logClass(params:string, p2?:string) {
return function(target: any) {
console.log("params",params);
console.log("p2",p2);
console.log("target",target);
}
}
被装饰的类:
@logClass("hello world", "第二个参数")
class HttpClient {
constructor() {
}
getData() {
}
}
运行结果:
三、属性装饰器
定义方式:
function 属性装饰器名() {
return function(target: any, attr: any) {
...
}
}
这里返回的 function
有两个参数:
- 对于静态成员来说是类的
构造函数
,对于实例成员来说是类的原型对象
- 成员的名字(
string
类型)
例如我们可以使用下面的方式使用属性装饰器来修改属性:
属性装饰器:
function logProperty(params: any) {
return function(target: any, attr: any) {
target[attr] = params;
}
}
被装饰的类:
// @logClass
class HttpClient {
@logProperty("hello world")
public apiUrl: string | undefined;
constructor() {
}
getData() {
}
}
打印属性:
let httpClient:HttpClient = new HttpClient();
console.log(httpClient.apiUrl);
运行结果:
可以看到属性的值被修改为了我们传入到注解中的值了。
四、方法装饰器
定义方式:
function 装饰器名(参数列表) {
return function(target:any, methodName:any, desc:any) {
...
}
}
这里返回的 function
有三个参数:
target
是修饰的类的对象原型methodName
是方法名desc
方法的描述信息对象
我们可以将它们打印出来:
function Get(url:string) {
return function(target:any, methodName:any, desc:any) {
console.log(target);
console.log(methodName);
console.log(desc);
}
}
class HttpClient {
constructor() {
}
@Get("http://www.itying.com")
getData() {
}
}
运行结果:
使用方法装饰器对方法进行扩展
被修饰类:
class HttpClient {
constructor() {
}
getData(...args:any[]) {
console.log(args);
}
}
此时我们希望扩展 getData()
方法的功能:在执行前后进行日志打印,并将传入的参数先转换为 string
类型。
具体的装饰器如下:
function logMethod() {
return function(target:any, methodName:any, desc:any) {
// 先保存原来的函数
let originMethod:Function = desc.value;
// 修改函数的实现
desc.value = function(...args:any[]) {
// 数据预处理(转为string)
args = args.map(value=>String(value));
// 运行前日志
console.log(`====函数${methodName}运行前====`);
// 调用原函数
originMethod.apply(this, args);
// 运行后日志
console.log(`====函数${methodName}运行后====`);
}
}
}
创建对象并调用方法:
let httpClient:HttpClient = new HttpClient();
httpClient.getData(1,2,3);
运行结果:
五、方法参数装饰器
定义方法:
function logParam(){
return function(target:any, methodName:any,paramIndex:any) {
...
}
}
定义方法和方法装饰器基本一致,区别在于三个参数不同:
target
是修饰的类的对象原型methodName
是方法名paramIndex
是参数的索引
示例:
function logParam(){
return function(target:any, methodName:any,paramIndex:any) {
console.log(target);
console.log(methodName);
console.log(paramIndex);
}
}
被修饰的类
class HttpClient {
// @logProperty("hello world")
public apiUrl: string | undefined;
constructor() {
}
getData(@logParam() str:string) {
console.log("getData函数被调用了");
}
}
运行结果:
六、装饰器的执行顺序
属性装饰器 => 方法装饰器 => 方法参数装饰器 => 类装饰器
TypeScript学习笔记(四)装饰器的更多相关文章
- java之jvm学习笔记四(安全管理器)
java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...
- Typescript 学习笔记四:回忆ES5 中的类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- python学习笔记之装饰器、递归、算法(第四天)
参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...
- Python学习笔记012——装饰器
1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...
- Python学习笔记:装饰器
Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...
- python学习笔记(五):装饰器、生成器、内置函数、json
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...
- python学习笔记:装饰器2
python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...
- Python学习笔记之装饰器原理
def decorator(fn): def wrapper(): print("询价") fn() print("购买成功!") return wrapper ...
- python学习笔记之装饰器、生成器、内置函数、json(五)
一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面 ...
- 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一
Grabage Collection GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...
随机推荐
- .NetCore使用Docker安装ElasticSearch、Kibana 记录日志
前言 最近园子里看到一篇<.Net Core with 微服务 - Elastic APM> 的文章(主要用于对接口的调用链.性能进行监控),非常实用,这里讲解.NetCore将日志写入E ...
- zabbix4.0升级到zabbix5.0
1 更新yum源 # yum erase zabbix-release-4.0-1.el7.noarch # rpm -ivh https://mirrors.aliyun.com/zabbix/za ...
- 阿里P7大佬带你解密Sentinel
概述 在接连写了两篇关于限流的文章(<面试补习>- 你来说说什么是限流?, 限流神器Sentinel,不了解一下吗?)后,总感觉还差最后一点内容来闭环整个限流相关的内容,这两天在翻查相关文 ...
- Gym - 101128E Wooden Signs DP
题目大意: 一共n块木板,前两个数给出最底下木块的两个端点,后面n-1个数给出第i层的一个固定端点,问你木块的所有放置情况. 分析: 状态: d[i][j]表示第i个木块,第i-1块木板的未固定端点为 ...
- Nginx:Nginx配置文件详解
Nginx是一款面向性能设计的HTTP服务器,相较于Apache.lighttpd具有占有内存少,稳定性高等优势. 如下是Nginx的配置详解: ######Nginx配置文件nginx.conf中文 ...
- [心得]安装MongoDB
1. 安装 (1) 其他默认 (2) 创建文件 在 E:\DevTools\MongoDB\Server\3.4 1 1 E:\DevTools\MongoDB\Server\3.4 下创建dat ...
- PHP经典算法之背包问题
问题:假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可得之总价物品,假设是水果好了,水果的编号.单价与重量如下所示: 1 栗子 4KG $4500 2 苹果 5KG $5700 3 ...
- Kotlin Coroutine(协程): 二、初识协程
@ 目录 前言 一.初识协程 1.runBlocking: 阻塞协程 2.launch: 创建协程 3.Job 4.coroutineScope 5.协程取消 6.协程超时 7.async 并行任务 ...
- MySQL | Xtrabackup 安装
rpm方式安装 # xtrabackup 2.4.8 安装包 wget https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup- ...
- 「AGC029C」Lexicographic constraints
「AGC029C」Lexicographic constraints 传送门 好像这个题非常 easy. 首先这个答案显然具有可二分性,所以问题转化为如何判定给定的 \(k\) 是否可行. 如果 \( ...