概念介绍

应用对象:类。
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的,并在保持类方法签名完整性的前提下,提供了额外的功能。
能够被附加到类声明、方法、访问符,属性或参数上,使用@expression的形式,@express求值后必须为一个函数,他会在运行时被调用,被装饰的声明信息作为PropertyDescriptor参数传入。

使用

ts的编译环境

  1. 必须在命令行或`tsconfig.json`里启用`experimentalDecorators`编译器选项。

node TS的开发环境搭建

  1. package.json创建: npm init -y
  2. 开发依赖安装: npm i typescript ts-node-dev tslint @types/node -D
  3. 启动脚本

    1. "scripts": {
    2. "start": "ts-node-dev ./src/index.ts -P tsconfig.json --no-cache",
    3. "build": "tsc -P tsconfig.json && node ./dist/index.js",
    4. "tslint": "tslint --fix -p tsconfig.json"
    5. }
  4. 加⼊tsconfig.json

    1. {
    2. "compilerOptions": {
    3. "outDir": "./dist",
    4. "target": "es2017",
    5. "module": "commonjs",//组织代码⽅式
    6. "sourceMap": true,
    7. "moduleResolution": "node", // 模块解决策略
    8. "experimentalDecorators": true, // 开启装饰器定义
    9. "allowSyntheticDefaultImports": true, // 允许es6⽅式import
    10. "lib": ["es2015"],
    11. "typeRoots": ["./node_modules/@types"],
    12. },
    13. "include": ["src/**/*"] }

    执行顺序

    1. @f
    2. @g
    3. x

    以上的写法相当于f(g(x)),当多个装饰器应用在一个声明上时会进行如下步骤的操作:

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

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

  • target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • propertyKey成员的名字
  • PropertyDescriptor成员的描述符

    例子1

    正常情况下,Maths类被实例化时候会执行log函数,log函数重写了add函数,所以函数的执行结果被改变了。 ``typescript console.log('日志应用和切面实现.....') function log(target, name, descriptor) { var oldValue = descriptor.value; console.log('descriptor',descriptor) descriptor.value = function () { console.log(Calling “${name}” with`, arguments); return oldValue.apply(null, arguments); } return descriptor; }

function checkLogin (target, name, descriptor) { const isLogin = true const comp = descriptor.value // this指向的是构造函数 descriptor.value = function(){ return isLogin ? comp.apply(null, arguments) : ‘请先登录’ } } class Maths { @log add(a, b) { return a + b; } @checkLogin comp (value){ return “123”+ value } } const math = new Maths() math.add(2, 4)

  1. <a name="uon2n"></a>
  2. # 例子2
  3. 实现Midway的@Get、 @querystring、body装饰器
  4. ```typescript
  5. import * as glob from 'glob';// 可以无限遍历目录
  6. import * as Koa from 'koa';
  7. import * as KoaRouter from 'koa-router';
  8. import * as Parameter from 'parameter'
  9. type HTTPMethod = 'get' | 'put' | 'del' | 'post' | 'patch'
  10. type LoadOptions = {
  11. extname?: string
  12. }
  13. type RouteOptions = {
  14. prefix?: string;
  15. middlewares?: Array<Koa.Middleware>
  16. }
  17. const router = new KoaRouter()
  18. const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {
  19. return (target, property: string) => {
  20. process.nextTick(() => {
  21. // 添加中间件数组
  22. const middlewares = []
  23. if (options.middlewares) {
  24. middlewares.push(...options.middlewares)
  25. }
  26. console.log('target',target)
  27. console.log('property',property)
  28. //这里是类里面的方法。
  29. console.log('target[property]',target[property])
  30. if (target.middlewares) {
  31. middlewares.push(...target.middlewares)
  32. }
  33. middlewares.push(target[property])
  34. const url = options.prefix ? options.prefix + path : path
  35. // router[method](url, target[property])
  36. // router.get('/user/:id', controller.user.info);
  37. router[method](url, ...middlewares)
  38. })
  39. }
  40. }
  41. const method = method => (path: string, options?: RouteOptions) => decorate(method, path, options, router)
  42. export const get = method('get')
  43. export const post = method('post')
  44. export const put = method('put')
  45. export const del = method('del')
  46. // 相当于@controller。
  47. export const load = (folder: string, options: LoadOptions = {}): KoaRouter => {
  48. const extname = options.extname || '.{js,ts}'
  49. glob.sync(require('path').join(folder, `./**/*${extname}`)).forEach((item) => require(item))
  50. return router
  51. }
  52. // @middlewares
  53. export const middlewares = (middlewares: Koa.Middleware[]) => {
  54. return function (target) {
  55. target.prototype.middlewares = middlewares
  56. }
  57. }
  58. const validateRule = paramPart => rule => {
  59. return function (target, name, descriptor) {
  60. const oldValue = descriptor.value
  61. descriptor.value = function () {
  62. const ctx = arguments[0]
  63. const p = new Parameter()
  64. const data = ctx[paramPart]
  65. const errors = p.validate(rule, data)
  66. console.log('error',errors)
  67. if (errors) throw new Error(JSON.stringify(errors))
  68. return oldValue.apply(null, arguments);
  69. }
  70. return descriptor;
  71. }
  72. }
  73. // @querystring
  74. export const querystring = validateRule('query')
  75. // @body
  76. export const body = validateRule('body')