装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能。

装饰器以 @expression 形式呈现在被装饰对象的前面或者上方,其中 expression 为一个函数,根据其所装饰的对象的不同,得到的入参也不同。

以下两种风格均是合法的:

  1. @f @g x
  1. @f
  2. @g
  3. x

ES 中装饰器处于 Stage 2 阶段 ,TypeScript 中通过开启相应编译开关来使用。

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

一个简单的示例

一个简单的示例,展示了 TypeScript 中如何编写和使用装饰器。

  1. function log(
  2. _target: any,
  3. propertyKey: string,
  4. descriptor: PropertyDescriptor
  5. ) {
  6. const originalMethod = descriptor.value;
  7. descriptor.value = function() {
  8. console.log(`method ${propertyKey} called`);
  9. return originalMethod.apply(this, arguments);
  10. };
  11. }
  12.  
  13. class Test {

  14. @log

  15. static sayHello() {

  16. console.log("hello");

  17. }

  18. }
  19.  
  20. Test.sayHello();

上面的示例中,创建了名为 log 的方法,它将作为装饰器作用于类的方法上,在方法被调用时输出一条日志。作为装饰器的 log 函数其入参在后面会介绍。

执行结果:

  1. method sayHello called
  2. hello

装饰器的工厂方法

上面的装饰器比较呆板,设想我们想将它变得更加灵活和易于复用一些,则可以通过创建一个工厂方法来实现。因为本质上装饰器就是个普通函数,函数可通过另外的函数来创建和返回,同时装饰器的使用本质上也是一个函数调用。通过传递给工厂方法不同的参数,以获得不同表现的装饰器。

  1. function logFactory(prefix: string) {
  2. return function log(
  3. _target: any,
  4. propertyKey: string,
  5. descriptor: PropertyDescriptor
  6. ) {
  7. const originalMethod = descriptor.value;
  8. descriptor.value = function() {
  9. console.log(`method ${propertyKey} called`);
  10. return originalMethod.apply(this, arguments);
  11. };
  12. };
  13. }
  14.  
  15. class Test {

  16. @logFactory("[debug]")

  17. static sayHello() {

  18. console.log("hello");

  19. }

  20. @logFactory("[info]")

  21. static sum() {

  22. return 1 + 1;

  23. }

  24. }
  25.  
  26. Test.sayHello();

  27. Test.sum();

执行结果:

  1. [debug] method sayHello called
  2. hello
  3. [info] method sum called

多个装饰器

多个装饰器可同时作用于同一对象,按顺序书写出需要运用的装饰器即可。其求值(evaluate)和真正被执行(call)的顺序是反向的。即,排在前面的先求值,排在最后的先执行。

譬如,

  1. function f() {
  2. console.log("f(): evaluated");
  3. return function(target, propertyKey: string, descriptor: PropertyDescriptor) {
  4. console.log("f(): called");
  5. };
  6. }
  7.  
  8. function g() {

  9. console.log("g(): evaluated");

  10. return function(target, propertyKey: string, descriptor: PropertyDescriptor) {

  11. console.log("g(): called");

  12. };

  13. }
  14.  
  15. class C {

  16. @f()

  17. @g()

  18. method() {}

  19. }

求值 的过程就体现在装饰器可能并不直接是一个可调用的函数,而是一个工厂方法或其他表达式,只有在这个工厂方法或表达式被求值后,才得到真正被调用的装饰器。

所以在这个示例中,先依次对 f() g() 求值,再从 g() 开始执行到 f()

运行结果:

  1. f(): evaluated
  2. g(): evaluated
  3. g(): called
  4. f(): called

不同类型的装饰器

类的装饰器

作用于类(Class)上的装饰器,用于修改类的一些属性。如果装饰器有返回值,该返回值将替换掉该类的声明而作为新的构造器使用。

装饰器入参:

  • 类的构造器。

示例:

  1. function sealed(constructor: Function) {
  2. Object.seal(constructor);
  3. Object.seal(constructor.prototype);
  4. }
  5.  
  6. @sealed

  7. class Greeter {

  8. greeting: string;

  9. constructor(message: string) {

  10. this.greeting = message;

  11. }

  12. greet() {

  13. return "Hello, " + this.greeting;

  14. }

  15. }

@sealed 将类进行密封,将无法再向类添加属性,同时类上属性也变成不可配置的(non-configurable)。

另一个示例:

  1. function classDecorator<T extends { new (...args: any[]): {} }>(
  2. constructor: T
  3. ) {
  4. return class extends constructor {
  5. newProperty = "new property";
  6. hello = "override";
  7. };
  8. }
  9.  
  10. @classDecorator

  11. class Greeter {

  12. property = "property";

  13. hello: string;

  14. constructor(m: string) {

  15. this.hello = m;

  16. }

  17. }
  18.  
  19. console.log(new Greeter("world"));

因为 @classDecorator 中有返回值,这个值将替换本来类的定义,当 new 的时候,使用的是装饰器中返回的构造器来创建类。

方法的装饰器

装饰器作用于类的方法时可用于观察,修改或替换该方法。如果装饰器有返回值,将替换掉被作用方法的属性描述器(roperty Descriptor)。

装饰器入参依次为:

  • 作用于静态方法时为类的构造器,实例方法时为类的原型(prototype)。
  • 被作用的方法的名称。
  • 被作用对象的属性描述器。

示例:

  1. function enumerable(value: boolean) {
  2. return function(
  3. target: any,
  4. propertyKey: string,
  5. descriptor: PropertyDescriptor
  6. ) {
  7. descriptor.enumerable = value;
  8. };
  9. }
  10.  
  11. class Greeter {

  12. greeting: string;

  13. constructor(message: string) {

  14. this.greeting = message;

  15. }
  16.  
  17. @enumerable(false)

  18. greet() {

  19. return "Hello, " + this.greeting;

  20. }

  21. }

上面示例中 @enumerable 改变了被装饰方法的 enumerable 属性,控制其是否可枚举。

类的方法可以是设置器(setter)或获取器(getter)。当两者成对出现时,应当只对其中一个运用装饰器,谁先出现就用在谁身上。因为装饰器应用时是用在 getset 两者合并的属性描述器上的。

  1. class Test {
  2. private _foo = 1;
  3. @logFactory("[info]")
  4. get foo() {
  5. return this._foo;
  6. }
  7. //
  8. TypeScript 装饰器的更多相关文章

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

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

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

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

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

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

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

      1. TypeScript 中装饰器的理解?应用场景?
      1. 一.是什么 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上 是一种在不改变原类和使用继承的情况下,动态地扩展对象功能 同样的,本质也不是什么高大上的结构,就是一个普通的 ...

      1. Typescript中的装饰器原理
      1. Typescript中的装饰器原理 1.小原理 因为react中的高阶组件本质上是个高阶函数的调用, 所以高阶组件的使用,我们既可以使用函数式方法调用,也可以使用装饰器. 也就是说,装饰器的本质就是一 ...

    1.  
    2. 随机推荐

        1. C#读写.ini文件
        1. 转载来源: http://blog.csdn.net/source0573/article/details/49668079 https://www.cnblogs.com/wang726zq/arc ...

        1. DotNetBar 第三方控件使用
        1. 1.BalloonTip(气泡提醒) 效果: 代码: balloonTip1.SetBalloonCaption(txtusername, "提示");            ba ...

        1. POST-GET请求
        1. 在应用中最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet.post与get的不同之处在于post的参数不是放在URL字串里面 ...

        1. sharepoint 2013 创建母版页
        1. 一.创建新的母版页, 并添加了新的样式表 1.从CodePlex 上获得Starter Master Pages for SharePoint 2010 或复制以下母版代码 <%@Master  ...

        1. [xdoj1029]求解某个数的最高位和最低位
        1. 解题关键: 1.最高位求法 long long int x=n^m; 式子两边同时取lg lg(x)=m*lg(n): x=10^(m*lg(n)): 10的整数次方的最高位一定是1,所以x的最高位取 ...

        1. vue 上传二进制图片
        1. 1.前段代码 <el-form-item label="证件照片" prop="idImage"> <input @change='ss' t ...

        1. C++使用RabbitMQ类库做客户端与RabbitMQ Server通讯,生成C++可调用的rabbimq.*.dll的过程
        1. Step: download the latest rabbitmq-c via: https://github.com/alanxz/rabbitmq-c follow the document, ...

        1. 使用jquery插件实现图片延迟加载--懒加载技术
        1. 原文链接:http://www.cnblogs.com/lei2007/archive/2013/05/31/3110725.html 感谢作者.以下为原文,备忘仅供自己学习. 第一:lazyLoad ...

        1. 删除docker私有仓库中的镜像
        1. 1.搭建私有仓库 (1)拉取私有仓库镜像 docker pull registry(2)启动私有仓库容器 docker run ‐di ‐‐name=registry ‐p 5000:5000 reg ...

        1. 39、生鲜电商平台-redis缓存在商品中的设计与架构
        1. 说明:Java开源生鲜电商平台-redis缓存在商品中的设计与架构. 1. 各种计数,商品维度计数和用户维度计数 说起电商,肯定离不开商品,而附带商品有各种计数(喜欢数,评论数,鉴定数,浏览数,etc ...