一、装饰器的作用

我个人的理解是: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 有两个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
  2. 成员的名字(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 有三个参数:

  1. target 是修饰的类的对象原型
  2. methodName 是方法名
  3. 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) {
...
}
}

定义方法和方法装饰器基本一致,区别在于三个参数不同:

  1. target 是修饰的类的对象原型
  2. methodName 是方法名
  3. 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学习笔记(四)装饰器的更多相关文章

  1. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  2. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  3. python学习笔记之装饰器、递归、算法(第四天)

    参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...

  4. Python学习笔记012——装饰器

    1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函 ...

  5. Python学习笔记:装饰器

    Python 装饰器的基本概念和应用 代码编写要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即: 封闭:已 ...

  6. python学习笔记(五):装饰器、生成器、内置函数、json

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...

  7. python学习笔记:装饰器2

    python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 一.一般装饰函数实例: import datetime def func_name(func):#定义一个装饰函数, ...

  8. Python学习笔记之装饰器原理

    def decorator(fn): def wrapper(): print("询价") fn() print("购买成功!") return wrapper ...

  9. python学习笔记之装饰器、生成器、内置函数、json(五)

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面 ...

  10. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一

    Grabage Collection      GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...

随机推荐

  1. Java实现适配器模式

    适配器模式(Adapter) 适配器模式涉及到3个角色:要被适配的接口,适配器,目标接口 适配器的工作就是将被适配的接口转换为目标接口 "鸭子类型"就是一个典型的适配器模式:如果它 ...

  2. UVA 10689 Yet another Number Sequence 矩阵快速幂 水呀水

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  3. MyBatis-HotSwap, MyBatis热部署

    https://github.com/xiaochenxinqing/MyBatis-HotSwap   1 https://github.com/xiaochenxinqing/MyBatis-Ho ...

  4. robotframework安装robotframework-requests库遇到的几种问题

    robotframework-requests库依赖于requests库,所以如果安装robotframework-requests库后,在RF中的RequestsLibrary不能使用或者使用pyt ...

  5. 使用Hugo框架搭建博客的过程 - 部署

    前言 完成前期的准备工作后,在部署阶段需要配置服务器或对象存储服务. 对象存储和服务器对比 对象存储平台 国内有阿里云OSS.腾讯COS.又拍云.七牛云等.国外有Github Pages.Netlif ...

  6. Adaptive AUTOSAR 学习笔记 3 - AP 背景、技术及特征(中文翻译)

    本系列学习笔记基于 AUTOSAR Adaptive Platform 官方文档 R20-11 版本.本文从AUTOSAR_EXP_PlatformDesign.pdf开始,一边学习,一边顺带着翻译一 ...

  7. GO系列-ini文件处理

    gopkg.in/ini.v1 配置加载 创建一个空的配置 cfg := ini.Empty() 直接加载存在的配置文件,如果文件不存在就会报错 cfg, err := ini.Load(" ...

  8. python使用笔记003-文件操作(一)

    文件操作分为: 1.打开文件,如果文件在当前目录下直接写文件名,如果文件在其他目录下写绝对路径 2.读/写文件 3.关闭文件 一.文件打开模式 # 'r':只读,文件读取后,会有文件指针记录读取文件的 ...

  9. IP数据包格式与ARP转发原理

    一.网络层简介1.网络层功能2.网络层协议字段二.ICMP与封装三.ARP协议与ARP欺骗1.ARP协议2.ARP欺骗 1.网络层功能 1. 定义了基于IP地址的逻辑地址2. 连接不同的媒介3. 选择 ...

  10. Node性能如何进行监控以及优化?

    一. 是什么 Node作为一门服务端语言,性能方面尤为重要,其衡量指标一般有如下: CPU 内存 I/O 网络 CPU 主要分成了两部分: CPU负载:在某个时间段内,占用以及等待CPU的进程总数 C ...