TypeScript学习笔记(九):装饰器(Decorators)

装饰器简介

装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。

需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变。

若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:

1 {
2     "compilerOptions": {
3         "target": "ES5",
4         "experimentalDecorators": true
5     }
6 }

如何定义

装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

装饰器组合

多个装饰器可以同时应用到一个声明上,就像下面的示例:

书写在同一行上:

@f @g x

书写在多行上:

@f
@g
x

在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:

  1. 由上至下依次对装饰器表达式求值。
  2. 求值的结果会被当作函数,由下至上依次调用。

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)。

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

我们来看一个例子:

 1 function sealed(constructor: Function) {
 2     Object.seal(constructor);
 3     Object.seal(constructor.prototype);
 4 }
 5
 6 @sealed
 7 class MyClass {
 8     a: number = 0;
 9     b: string = "hello";
10 }
11
12 var obj = new MyClass();
13 // obj.c = true; // 编译报错

通过类装饰器我们可以对类的原型对象做一定的修改。

方法装饰器

方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。

方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符。

注意:如果代码输出目标版本小于ES5,属性描述符将会是undefined。

我们来看下面的例子:

 1 function methodDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
 4     };
 5 }
 6
 7 class MyClass {
 8     @methodDecorator(true, "this is static")
 9     public static sFunc(): void {
10         console.log("call static method");
11     }
12
13     @methodDecorator(false)
14     public func(): void {
15         console.log("call method");
16     }
17 }
18
19 MyClass.sFunc();
20 MyClass.sFunc();
21
22 var obj = new MyClass();
23 obj.func();
24 obj.func();

输出如下:

1 false, undefined, [object Object], func, {"writable":true,"enumerable":true,"configurable":true}
2 true, this is static, function MyClass() {
3     }, sFunc, {"writable":true,"enumerable":true,"configurable":true}
4 call static method
5 call static method
6 call method
7 call method

我们可以发现,方法装饰器返回的函数会在解释类的对应方法时被调用一次,并可以得到装饰器的参数和被装饰的方法的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的方法不会触发装饰器方法。

访问器装饰器

访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符。

注意:如果代码输出目标版本小于ES5,Property Descriptor将会是undefined。

同时TypeScript不允许同时装饰一个成员的get和set访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个属性描述符时,它联合了get和set访问器,而不是分开声明的。

我们来看一个例子:

 1 function methodDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey + ", " + JSON.stringify(descriptor));
 4     };
 5 }
 6
 7 class MyClass {
 8     private static _myName: string;
 9
10     @methodDecorator(true, "this is static")
11     public static set myName(value: string) {
12         this._myName = value;
13     }
14
15     public static get myName(): string {
16         return this._myName;
17     }
18
19     private _age: number;
20
21     @methodDecorator(false)
22     public set age(value: number) {
23         this._age = value;
24     }
25
26     public get age(): number {
27         return this._age;
28     }
29 }
30
31 MyClass.myName = "hello";
32 console.log(MyClass.myName);
33
34 var obj = new MyClass();
35 obj.age = 28;
36 console.log(obj.age);

输出如下:

1 false, undefined, [object Object], age, {"enumerable":true,"configurable":true}
2 true, this is static, function MyClass() {
3     }, myName, {"enumerable":true,"configurable":true}
4 hello
5 28

我们可以发现,访问器装饰器返回的函数会在解释类的对应访问器时被调用一次,并可以得到装饰器的参数和被装饰的访问器的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的访问器不会触发装饰器方法。

属性装饰器

属性装饰器声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。

我们来看一个例子:

 1 function propDecorator(param1: boolean, param2?: string) {
 2     return function (target: any, propertyKey: string) {
 3         console.log(param1 + ", " + param2 + ", " + target + ", " + propertyKey);
 4     };
 5 }
 6
 7 class MyClass {
 8     @propDecorator(false, "Hi")
 9     public static A: number = 0;
10
11     @propDecorator(true)
12     public a: string = "hello";
13 }
14
15 console.log(MyClass.A);
16 var obj = new MyClass();
17 console.log(obj.a);

输出如下:

1 true, undefined, [object Object], a
2 false, Hi, function MyClass() {
3         this.a = "hello";
4     }, A
5 0
6 hello

我们可以发现,属性装饰器返回的函数会在解释类的对应属性时被调用一次,并可以得到装饰器的参数和被装饰的属性的相关信息。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的属性不会触发装饰器方法。

参数装饰器

参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如 declare的类)里。

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。

我们来看一个例子:

 1 function paramDecorator(target: Object, propertyKey: string | symbol, parameterIndex: number) {
 2     console.log(target + ", " + <any> propertyKey + ", " + parameterIndex);
 3 }
 4
 5 class MyClass {
 6     public func(@paramDecorator a: number, @paramDecorator b: string = "hello", @paramDecorator c?: boolean): void {
 7         console.log("call method");
 8     }
 9 }
10
11 var obj = new MyClass();
12 obj.func(1);
13 obj.func(2);

输出如下:

1 [object Object], func, 2
2 [object Object], func, 1
3 [object Object], func, 0
4 call method
5 call method

我们可以发现,参数装饰器返回的函数会在解释方法的参数时被调用一次,并可以得到参数的相关信息。我们有3个参数就调用了3次。

装饰器方法的调用只会在加载代码时执行一次,调用被装饰的参数的方法不会触发装饰器方法。

时间: 2024-08-06 11:36:38

TypeScript学习笔记(九):装饰器(Decorators)的相关文章

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

这周学习了装饰器和生成器,写下博客,记录一下装饰器和生成器相关的内容. 一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面每个函数都加一个功能,用来统计每个函数的运行时间是多少,找出来运行比较慢的函数,来优化代码,就需要添加一个新的功能,来统计程序的运行时间,那这样的话,就得修改每个函数了,需要改代码,但是代码特别多,改完了公司倒闭了,这时候装饰

python学习笔记之——装饰器及对象

1.反射之__import__ 我们知道import语句是用来导入外部模块的,当然还有from...import...也可以,但是其实import实际上是使用builtin函数__import__来工作的.        在一些程序中,我们可以动态地去调用函数,如果我们知道模块的名称(字符串)的时候,我们可以很方便的使用动态调用. __import__(module_name[, globals[, locals[, fromlist]]]) #可选参数默认为globals(),locals()

python学习笔记:装饰器2

python的装饰器本质是函数,为了不改变装饰目标函数内部代码而增加额外功能而存在 直接举例: import datetime def func_name(func):#定义一个装饰函数,接受一个函数对象作为参数(也就是被装饰的函数) def wrap():#包装函数 print("Function name:%s"%(func.__name__)) func() #执行目标函数 return wrap #返回包装函数 @func_name #等于 func_time = func_n

Python学习笔记012——装饰器

1 装饰器 1.1装饰器定义 在代码运行期间动态增加功能的方式,称之为"装饰器"(Decorator). 1.2 装饰器分类 装饰器:函数装饰器,类装饰器,函数的装饰器,类的装饰器 装饰器:函数装饰函数,函数装饰类,类装饰函数,类装饰类(两者不是一一对应关系,其实我也不知道他们之间是什么样的对应关系) 一般而言,主要应用的是函数装饰函数 1.3 装饰器本质 本质上,decorator就是一个返回函数的高阶函数. 在面向对象(OOP)的设计模式中,decorator被称为装饰模式.OOP

python学习笔记之装饰器(语法糖)

什么是装饰器 装饰器的知识点铺垫(函数即变量,高阶函数,嵌套函数) 不带参数的装饰器示例 带参数的装饰器示例 作业 一.什么是装饰器 本质上,装饰器就是返回一个函数的高阶函数.装饰器就是一个函数 装饰器的原则: 不修改被装饰对象的源代码 不修改被装饰对象的调用方式 二.装饰器涉及的知识点 函数即变量 高阶函数 嵌套函数 函数即变量: 在python中,一个变量首先被定义,分配内存空间,然后再使用. 以x=1,这个简单的赋值语句为例子.首先在内存中分配一个空间,x指向该内存空间,该内存空间内存入"

APUE 学习笔记(九) 高级I/O

1. 非阻塞I/O 低速系统调用时可能会使进程永远阻塞的一类系统调用,包括以下调用: (1)某些文件类型你(网络socket套接字.终端设备.管道)暂无可使用数据,则读操作可能会使调用者永远阻塞 (2)如果数据不能立即被(1)中文件类型接受,则写操作会使调用者永远阻塞 (3)某些进程间通信函数 非阻塞I/O使我们可以调用open.read.write这样的I/O操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回 对于一个给定的文件有两种方法对其指定非阻塞I/O: (1)调用

python学习笔记九——文件与目录

1.python进行文件读写的函数是open或file类 mode:r  只读 r+   读写 w  写入,先删除原文件,再重新写入,如果文件没有则创建 w+  读写,先删除原文件,再重新写入,如果文件没有则创建(可写入和输出) a  写入,在文件末尾追加新的内容,文件不存在则创建 a+  读写,在文件末尾追加新的内容,文件不存在则创建 b  打开二进制文件,可与r,w,a,+结合使用 U  支持所有的换行符号,"\r","\n","\r\n"

angular学习笔记(九)-css类和样式3

再来看一个选择li列表的例子: 点击li中的任意项,被点击的li高亮显示: <!DOCTYPE html> <html ng-app> <head> <title>6.3css类和样式</title> <meta charset="utf-8"> <script src="../angular.js"></script> <script src="scri

angular学习笔记(九)-css类和样式2

在上一个例子中,元素的类名使用拼接的方法,这样,类名中就不得不带有true或false,并且不易维护,所以,angular使用ng-class属性来控制元素的类名: 我们来看一个小例子,点击error按钮,顶部提示错误框,点击warning按钮,顶部提示警告框. 错误框的类名是.err,警告框的类名是.warn: <!DOCTYPE html> <html ng-app> <head> <title>6.2css类和样式</title> <

Python学习之路-装饰器&生成器&正则表达式

装饰器 通俗的讲,装饰器就是在不改变源代码基础上,给源代码增加新功能. 不改变函数的源代码.调用方式.返回值等,给函数增加新功能. 经典案例:登录装饰器, def login_decorator(func):     def inner():         if USER_TEMP["status"] == False:             print("\033[31;1m用户未登录,请先登录\033[0m")             login_atm()