TS装饰器介绍

装饰器模式简介

装饰器模式是一种重要的设计模式,它能够以对客户透明的方式动态地给一个对象附加上更多的责任,为明晰它的概念,请看下面的例子:

在一个末日生存游戏里,你有一个安全屋和一张床,每次睡觉都可以恢复一定的精力,但是初始的床什么都没有,每天睡在木板上只能恢复20点精力,某天你出外搜寻了材料并制作了一个床垫,将床垫放在床上,每次睡觉可以额外恢复20点精力,之后你又找到了被絮和三件套,甚至还给床进行改装增加了按摩功能,每次睡觉可以额外恢复的精力越来越多了…

在这个例子中,对于使用床的人而言,“睡觉”这个动作没有发生改变,床还是那张床,只不过我们通过床垫、被絮、按摩机增加了额外的功能。在程序设计中,它们就可以分别以装饰器的形式进行设计,再通过组合使床拥有全部的装饰特征,程序中类之间的关系使用uml类图表示如下:

uml

JavaScript的装饰器提案历经一波三折,目前仍处于Stage 2阶段,而且在语法和实现上经历了较大的改版,距离正式成为ECMA语言标准尚需时日。在TypeScript愈发流行的今天,它已推出了这个实验性功能,一些框架如angular、nestjs都已经大量使用了装饰器,本文我们一起探索一下它。

起步

新建一个node项目,并使用tsc工具生成tsconfig.json配置文件,笔者使用的tsc版本为4.0.3

mkdir decorator-tour && cd decorator-tour && npm init -y && tsc --init

为了使Typescript编译器支持装饰器,需要在tsconfig.json的compilerOptions选项中设置"experimentalDecorators": true

// tsconfig.json
{
   
  "compilerOptions": {
   
    ...
    "experimentalDecorators": true,
    ...
  }
}

新建index.ts文件,并输入以下最初的代码:

class Greeter {
   
  greeting: string;
  constructor(message: string) {
   
    this.greeting = message;
  }
  greet(name: string): string {
   
    console.log(`welcome, ${
     name}!`);
    return "Hello";
  }
}

const g = new Greeter('msg');

g.greet('tom');

这段代码声明了一个向顾客问好的类GreeterGreeter.greet方法表示“欢迎”之意,实现上就是打印一段欢迎文本出来。
执行tsc && node index.js后控制台将打印出welcome, tom!

装饰器核心玩法

装饰方法

“欢迎”应该配合上“微笑”,下面的需求是在调用greet之前先打印一行"smile"文本,怎么操作呢?当然可以直接在greet方法里加入打印"smile"的代码,但这并不好,试想未来可能实现Greeter.guideGreeter.interpret等函数,它们都需要先打印"smile"然后执行功能,这种情况下Greeter类的很多方法都有相同的需求,就可以将打印"smile"这个功能提取成一个装饰器。

function smile(
  target: Greeter,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
   
  return {
   
    ...descriptor,
    value: function(name: string) {
   
      console.log('smile');
      // 调用被装饰的方法
      return descriptor.value(name);
    }
  };
}

class Greeter {
   
  greeting: string;
  constructor(message: string) {
   
    this.greeting = message;
  }
  @smile
  greet(name: string): string {
   
    console.log(`welcome, ${
     name}!`);
    return "Hello";
  }
}

const g = new Greeter('msg');

g.greet('tom');
g.greet('tom');
g.greet('tom');

在上面的代码中,smile是一个装饰器,@smile语法规定了greet方法将使用smile装饰器,而装饰器smile本身其实是一个函数,它接收target(被装饰的对象),propertyKey(被装饰的属性名称)和descriptor(该属性的描述)作为参数,本例中target表示Greeter的原型对象,即Greeter.prototype,propertyKey是"greet",而descriptor是Greeter.prototype.greet的属性描述对象,类似下面这样:

{
   
  value: Function,
  writable: true,
  enumerable: true,
  configurable: true
}

那么在执行过程中是怎么把装饰器和原方法串联起来的呢?我们不妨分析一下经过tsc编译后的代码:

'use strict'
function smile(target, propertyKey, descriptor) {
   
    return __assign(__assign({
   }, descriptor), {
    value: function (name) {
   
            console.log('smile');
            return descriptor.value(name);
        } });
}
var Greeter = (function () {
   
  function Greeter(message) {
   
    this.greeting = message
  }
  Greeter.prototype.greet = function (name) {
   
    console.log('welcome, ' + name + '!')
    return 'Hello'
  }
  // @smile装饰器被解析成这一行
  __decorate([smile], Greeter.prototype, 'greet', null)
  return Greeter
})(
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值