装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,可以修改类的行为。 装饰器使用 @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. MAMP升级mysql5.6到5.7

    RT 1.先把mamp环境停掉 sudo sh /Applications/MAMP/bin/stop.sh 2.然后使用brew安装mysql5.7 brew install mysql 3.默认安 ...

  2. Spring 自动装配及自动注册的相关配置

    Spring支持好几种自动装配(Autowiring)的方式,以及自动扫描并注册Bean的配置(在beans.xml中配置). 下文我们进行一个小结. 1. <context: annotati ...

  3. unity创建和加载AssetBundle

    先说一下为什么要使用AssetBundle吧,以前做东西一直忽略这个问题,现在认为这个步骤很重要,代码是次要的,决策和为什么这样搞才是关键. 一句话概括吧,AssetBundle实现了资源与服务分离, ...

  4. Mobiscroll的介绍【一款兼容PC和移动设备的滑动插件】

    Mobiscroll是一个用于触摸设备的日期和时间选择器,它的使用不会改变HTML5.PhoneGap以及混合应用的原生用户体验.作为一款jQuery滑动选择插件,用户可以自定义主题样式,为自己的移动 ...

  5. php中const与define的区别

    1 版本差异: const 要求php的版本>5.3.0 define 可以兼容php4,php5 等版本 2 定义的位置区别: const关键字定义的常量是在编译时定义的,因此const关键字 ...

  6. ASP.NET MVC框架开发系列教程

    本系列教程是自己在工作中使用到而记录的,如有错误之处,请给与指正 文章目录 MVC4 开篇 第一章 初识MVC4 第二章 下山遇虎(@helper) 第三章 Models模块属性详解 第四章 在MVC ...

  7. Head First设计模式之桥接模式

    一.定义 桥接模式(Bridge Pattern),将抽象部分与它的实现部分分离,使的抽象和实现都可以独立地变化. 主要解决:在多维可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活. 何时使 ...

  8. 项目实战10.1—企业级自动化运维工具应用实战-ansible

    实战环境: 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备.公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测 ...

  9. 【Java框架型项目从入门到装逼】第七节 - 学生管理系统项目搭建

    本次的教程是打算用Spring,SpringMVC以及传统的jdbc技术来制作一个简单的增删改查项目,对用户信息进行增删改查,就这么简单. 1.新建项目 首先,打开eclipse,新建一个web项目. ...

  10. Python File flush方法应用

    # flush()使用# #!/usr/bin/python# # -*- coding: UTF-8 -*-## # 打开文件# fo = open("runoob.txt", ...