装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,可以修改类的行为。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

例:

@Path('/hello')
class HelloService {}

在TypeScript中装饰器还属于实验性语法,所以要想使用必须在配置文件中tsconfig.json编译选项中开启:

{
"compilerOptions": {
"experimentalDecorators": true
}
}

如何定义装饰器

装饰器本身其实就是一个函数,理论上忽略参数的话,任何函数都可以当做装饰器使用。例:

demo.ts

function Path(target:any) {
console.log("I am decorator.")
} @Path
class HelloService {}

使用tsc编译后,执行命令node demo.js,输出结果如下:

I am decorator.

装饰器执行时机

修饰器对类的行为的改变,是代码编译时发生的(不是TypeScript编译,而是js在执行机中编译阶段),而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。

在Node.js环境中模块一加载时就会执行

函数柯里化解决参数问题

但是实际场景中,有时希望向装饰器传入一些参数, 如下:

@Path("/hello", "world")
class HelloService {}

此时上面装饰器方法就不满足了(VSCode编译报错),这是我们可以借助JavaScript中函数柯里化特性

function Path(p1: string, p2: string) {
return function (target) { // 这才是真正装饰器
// do something
}
}

五种装饰器

在TypeScript中装饰器可以修饰四种语句:类,属性,访问器,方法以及方法参数。

1 类装饰器

应用于类构造函数,其参数是类的构造函数。

注意class并不是像Java那种强类型语言中的类,而是JavaScript构造函数的语法糖。

function Path(path: string) {
return function (target: Function) {
!target.prototype.$Meta && (target.prototype.$Meta = {})
target.prototype.$Meta.baseUrl = path;
};
} @Path('/hello')
class HelloService {
constructor() {}
} console.log(HelloService.prototype.$Meta);// 输出:{ baseUrl: '/hello' }
let hello = new HelloService();
console.log(hello.$Meta) // 输出:{ baseUrl: '/hello' }
2 方法装饰器

它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。

方法装饰会在运行时传入下列3个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、成员的名字。
  • 3、成员的属性描述符。
function GET(url: string) {
return function (target, methodName: string, descriptor: PropertyDescriptor) {
!target.$Meta && (target.$Meta = {});
target.$Meta[methodName] = url;
}
} class HelloService {
constructor() { }
@GET("xx")
getUser() { }
} console.log((<any>HelloService).$Meta);

注意:在vscode编辑时有时会报作为表达式调用时,无法解析方法修饰器的签名。错误,此时需要在tsconfig.json中增加target配置项:

{
"compilerOptions": {
"target": "es6",
"experimentalDecorators": true,
}
}
3 方法参数装饰器

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

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、参数的名字。
  • 3、参数在函数参数列表中的索引。
function PathParam(paramName: string) {
return function (target, methodName: string, paramIndex: number) {
!target.$Meta && (target.$Meta = {});
target.$Meta[paramIndex] = paramName;
}
} class HelloService {
constructor() { }
getUser( @PathParam("userId") userId: string) { }
} console.log((<any>HelloService).prototype.$Meta); // {'0':'userId'}
4 属性装饰器

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

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 2、成员的名字。
function DefaultValue(value: string) {
return function (target: any, propertyName: string) {
target[propertyName] = value;
}
} class Hello {
@DefaultValue("world") greeting: string;
} console.log(new Hello().greeting);// 输出: world

装饰器加载顺序

function ClassDecorator() {
return function (target) {
console.log("I am class decorator");
}
}
function MethodDecorator() {
return function (target, methodName: string, descriptor: PropertyDescriptor) {
console.log("I am method decorator");
}
}
function Param1Decorator() {
return function (target, methodName: string, paramIndex: number) {
console.log("I am parameter1 decorator");
}
}
function Param2Decorator() {
return function (target, methodName: string, paramIndex: number) {
console.log("I am parameter2 decorator");
}
}
function PropertyDecorator() {
return function (target, propertyName: string) {
console.log("I am property decorator");
}
} @ClassDecorator()
class Hello {
@PropertyDecorator()
greeting: string; @MethodDecorator()
greet( @Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
}

输出结果:

I am property decorator
I am parameter2 decorator
I am parameter1 decorator
I am method decorator
I am class decorator

从上述例子得出如下结论:

1、有多个参数装饰器时:从最后一个参数依次向前执行

2、方法和方法参数中参数装饰器先执行。

3、类装饰器总是最后执行。

4、方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行。上述例子中属性和方法调换位置,输出如下结果:

I am parameter2 decorator
I am parameter1 decorator
I am method decorator
I am property decorator
I am class decorator

TypeScript装饰器(decorators)的更多相关文章

  1. 从C#到TypeScript - 装饰器

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  2. 基于TypeScript装饰器定义Express RESTful 服务

    前言 本文主要讲解如何使用TypeScript装饰器定义Express路由.文中出现的代码经过简化不能直接运行,完整代码的请戳:https://github.com/WinfredWang/expre ...

  3. Angular 个人深究(一)【Angular中的Typescript 装饰器】

    Angular 个人深究[Angular中的Typescript 装饰器] 最近进入一个新的前端项目,为了能够更好地了解Angular框架,想到要研究底层代码. 注:本人前端小白一枚,文章旨在记录自己 ...

  4. 【低门槛 手把手】python 装饰器(Decorators)原理说明

    本文目的是由浅入深地介绍python装饰器原理 装饰器(Decorators)是 Python 的一个重要部分 其功能是,在不修改原函数(类)定义代码的情况下,增加新的功能 为了理解和实现装饰器,我们 ...

  5. TypeScript 装饰器

    装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能. 装饰器以 @expression 形式呈现在被装饰对象的前面或者上方,其中 ex ...

  6. TypeScript 装饰器的执行原理

    装饰器本质上提供了对被装饰对象 Property​ Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...

  7. Python装饰器(Decorators )

    http://book.pythontips.com/en/latest/decorators.html 在<Built-in Functions(3.6)>和<Python上下文管 ...

  8. Python 装饰器(Decorators) 超详细分类实例

        Python装饰器分类 Python 装饰器函数: 是指装饰器本身是函数风格的实现; 函数装饰器: 是指被装饰的目标对象是函数;(目标对象); 装饰器类 : 是指装饰器本身是类风格的实现; 类 ...

  9. typescript装饰器 方法装饰器 方法参数装饰器 装饰器的执行顺序

    /* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...

随机推荐

  1. Java基础--二进制运算

    1. System.out.println((byte)0x8f); 结果是? 2.System.out.println((byte)(0xc5>>1)); 结果是? 3.System.o ...

  2. Oracle 启动参数

    查看数据库的SID DB_NAME SERVICE_NAME

  3. centos7 部署 ELK 日志系统

    =============================================== 2017/12/24_第3次修改                       ccb_warlock 更 ...

  4. 顶点/片元 shader 总结

    Cg顶点程序必须在结构中传递顶点数据.几种常用的顶点结构定义在文件UnityCG.cginc中,有如下三种结构体: 1.appdata_base: 包含顶点位置,法线和一个纹理坐标.2.appdata ...

  5. 【转】搭建spark环境 单机版

    本文将介绍Apache Spark 1.6.0在单机的部署,与在集群中部署的步骤基本一致,只是少了一些master和slave文件的配置.直接安装scala与Spark就可以在单机使用,但如果用到hd ...

  6. windows c++ 错误汇总

    1.fatal error C1900 错误:fatal error C1900: “P1”(第“20081201”版)和“P2”(第“20080116”版)之间 Il 不匹配 检查之后发现jepgl ...

  7. robotframework学习笔记(七)------筛选执行用例

    第一种:手动勾选用例 可勾选用户,然后点击运行,这样就只运行到勾选的用例.如果不勾选的点击运行就会运行所有用例. 第二种 菜单中去筛选 可在菜单中去筛选勾用例,然后点击运行 Select All Te ...

  8. TurnipBit—MicroPython开发板:从积木式编程语言开始学做小小创客

    编程.建模.制作动画和游戏--这些当初我们默认只有成年人玩得转的事情,现在早已经被无数小孩子给颠覆甚至玩出新境界了.热爱科技和动手的"创客"(Maker)现在在全世界都炙手可热.今 ...

  9. System.Windows.Forms.PropertyGrid的使用

    PropertyGrid 控件简介 .NET 框架 PropertyGrid 控件是 Visual Studio .NET 属性浏览器的核心.PropertyGrid 控件显示对象或类型的属性,并主要 ...

  10. SuperSocket入门(四)-命令行协议

         前面已经了解了supersocket的一些基本的属性及相关的方法,下面就进入重点的学习内容,通信协议.在没有看官方的文档之前,对于协议的理解首先想到的是TCP和UDP协议.TCP 和 UDP ...