装饰器&元数据

装饰器

环境

  • ts包
npm i typescript
  • 初始化tscofng.json配置
tsc  --init

并开启装饰器实验功能:

"experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */,
 "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,

基本使用

类的装饰器

基本使用形式

// 类装饰器
const moveDecorator: ClassDecorator = function (target: Function) {
  console.log("target", target);
  target.prototype.move = function (pos: { x: number; y: number }) {
    console.log("move");
    return pos;
  };
};

@moveDecorator
class Tank {
  // move() {}
}

const tank1 = new Tank();
console.log((<any>tank1).move({ x: 1, y: 2 })); // { x: 1, y: 2 }

对装饰器的剖析:

  • 装饰器类:Dectorator
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;

可以看出这个装饰器是一个函数,然后它是继承与Function的,参数接一个函数,而这个函数就类的构造函数

// 类装饰器
const moveDecorator: ClassDecorator = function (target: Function) {
  console.log("target", target); // target的类型就是class Tank
};
@moveDecorator
class Tank {
}
  • 本质

先看代码

const moveDecorator: ClassDecorator = function (target: Function) {
  console.log("target", target);
};

class Tank {
}
moveDecorator(Tank);

这样执行的结果和上面使用**@形式**的结果是一摸一样的

因此装饰器其实就是一个语法糖,让我们更加便捷的来去修饰构造函数原型。并没有什么黑魔法

装饰器的叠加

没什么特殊写法,主需要在类名上面,一层层叠加装饰器函数就可以装饰。

// 类装饰器

const moveDecorator: ClassDecorator = function (target: Function) {
  // console.log("target", target);
  target.prototype.getPos = function (pos: { x: number; y: number }) {
    console.log("move");
    return pos;
  };
};

const musicDecorator: ClassDecorator = function (target: Function) {
  target.prototype.getMusic = function (music: string) {
    console.log("paly music");
    return music;
  };
};
@moveDecorator
@musicDecorator
class Tank {
  // move() {}
}

const tank1 = new Tank();
console.log((<any>tank1).getPos({ x: 1, y: 2 })); //{ x: 1, y: 2 }

(<any>tank1).getMusic("music"); //paly music

装饰器工厂

可以通过工厂函数,根据传递不同的类型参数,来返回不同的装饰器。

// 工厂装饰器
const MusicDecoratorFactory = function (type: string) {
  switch (type) {
    case "Tank":
      return function (target: Function) {
        target.prototype.getMusic = function (music: string) {
          console.log("Tank-music");
          return music;
        };
      };
    default:
      return function (target: Function) {
        target.prototype.getMusic = function (music: string) {
          console.log("default-music");
          return music;
        };
      };
  }
};

const musicDecorator: ClassDecorator = function (target: Function) {
  target.prototype.getMusic = function (music: string) {
    console.log("paly music");
    return music;
  };
};

@MusicDecoratorFactory("Tank")
class Tank {
  // move() {}
}

const tank1 = new Tank();

(<any>tank1).getMusic("music"); //tank-music

@MusicDecoratorFactory("dadada")
class SU7 {
  // move() {}
}

const su7 = new SU7();
(<any>su7).getMusic("music"); //default-music

方法装饰器

方法装饰器有三个参数

  • target:方法所属类的原型
  • PropertyKey:方法名称
  • descriptor:和对象劫持得到的可配置参数一致
{
  value: [Function: showsss],
  writable: true,
  enumerable: false,
  configurable: true
}
  • 示例
const showDecorator: MethodDecorator = (target, propertyKey, descriptor) => {
  console.log(target); //方法所属类的原型
      console.log(target.constructor === Person) //true
  console.log(propertyKey); //方法名字 show
  console.log(descriptor); //方法本体,也就是方法本身,是一个函数
  //执行descriptor.value(),执行方法
  (descriptor as any).value = () => {
    console.log("showDecorator");
  }; // 覆盖方法
  (descriptor as any).value(); // 执行方法
};

class Person {
  public static staticMethod() {
    console.log("staticMethod");
  }
  @showDecorator
  showsss() {
    console.log("show");
  }
}

const p = new Person();
p.showsss(); //showDecorator
静态方法装饰

与方法装饰器不同的是target是类即构造函数,不再试方法所属类的原型

const showDecorator: MethodDecorator = (
  target,
  propertyKey,
  descriptor: PropertyDescriptor
) => {
  console.log(target); //静态方法下target是构造函数
  console.log(target === Person); //true
  console.log(propertyKey); //方法名字 show
  console.log(descriptor); //方法本体,也就是方法本身,是一个函数
  //执行descriptor.value(),执行方法
  (descriptor as any).value = () => {
    console.log("showDecorator");
  }; // 覆盖方法
  (descriptor as any).value(); // 执行方法
  descriptor.writable = false;
};

class Person {
  @showDecorator
  public static staticMethod() {
    console.log("staticMethod");
  }
}
Person.staticMethod();

Person.staticMethod = () => {
  console.log("staticMethod999");
}; //报错 Cannot assign to read only property 'staticMethod' of function 'class Person

demo:输出高亮
const heightDecorator: MethodDecorator = (
  target,
  propertyKey,
  descriptor: TypedPropertyDescriptor<any>
) => {
  const method = descriptor.value;
  descriptor.value = () => {
    return `<div style="color: red">${method()}</div>`;
  };
};

class Demo {
  @heightDecorator
  public say() {
    return "hello";
  }
}

console.log(new Demo().say());
延迟执行装饰器
const delayDecoratorFactory =
  (delay: number): MethodDecorator =>
  (...args: any[]) => {
    const [, , descriptor] = args;
    const method = descriptor.value;
    descriptor.value = () => {
      setTimeout(() => {
        console.log("delayDecoratorFactory");
        method();
      }, delay);
    };
  };

class Test {
  @delayDecoratorFactory(1000)
  test() {
    console.log("test");
  }
}

new Test().test();

属性装饰器

和方法装饰器差不多,有两个参数

  • 类型
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
  • 示例
// 属性装饰器

const ProperDecorator: PropertyDecorator = (
  target: Object,
  key: string | symbol
) => {
  console.log(target === Person.prototype); // true
  console.log(target);
  console.log(key); // name
};

class Person {
  @ProperDecorator
  name: string = "张三";
}

console.log(new Person().name);
export {};
demo:属性值自动变小写
// 属性装饰器

const ProperDecorator: PropertyDecorator = (
  target: Object,
  key: string | symbol
) => {
  let name: string;
  Object.defineProperty(target, key, {
    get() {
      return name.toLocaleLowerCase();
    },
    set(value: string) {
      name = value;
    },
  });
  console.log(target === Person.prototype); // true
  console.log(target);
  console.log(key); // name
};

class Person {
  @ProperDecorator
  name: string = "DONG";
}

console.log(new Person().name);
export {};

参数装饰器

元数据
  • 安装
npm i reflect-metadata
  • 使用
    • 存储数据:Reflect.defineMetadata()
function defineMetadata(metadataKey: any, metadataValue: any, target: Object, propertyKey: string | symbol): void;
  • 获取数据:Reflect.getMetadata()
function getMetadata(metadataKey: any, target: Object, propertyKey: string | symbol): any;
  • 检查元数据是否存在Reflect.hasMetadata(metadataKey, target, propertyKey?)
function hasMetadata(metadataKey: any, target: Object): boolean;
  • 获取目标对象自身的元数据:Reflect.getOwnMetadata(metadataKey, target, propertyKey?)
 function getOwnMetadata(metadataKey: any, target: Object): any;
  • 检查目标对象自身是否具有元数据(不包括从原型链继承的元数据)Reflect.hasOwnMetadata(metadataKey, target, propertyKey?)
function hasOwnMetadata(metadataKey: any, target: Object): boolean;
  • demo
//存数据
Reflect.defineMetadata("required", requiredParams, target, propertyKey || "");
const requiredParams: number[] =
    Reflect.getMetadata("required", target, propertyKey || "") || [];
参数装饰器

和方法装饰器差不多,看类型声明

参数:

  • target:方法所属类的原型
  • propertyKey:方法的key
  • ParameterIndex:参数索引
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void;

修饰位置:参数前面

class User {
  getUser(@requireDecorator id: number) {
    console.log("-------------");
    console.log(id);
    console.log(this.getUser);
    return id;
  }
}
对参数传递不对时进行抛出错误
import "reflect-metadata";

// 参数装饰器:用来装饰函数的参数
const requireDecorator: ParameterDecorator = (
  target: Object,
  propertyKey: string | symbol | undefined,
  parameterIndex: number
) => {
  //   console.log(target === User.prototype); //true
  console.log(propertyKey); //getUser 调用的方法名
  console.log(parameterIndex); //0 调用的参数索引
  // 存放参数索引
  const requiredParams: number[] = [];
  requiredParams.push(parameterIndex);

  // 把参数索引保存到元数据中
  Reflect.defineMetadata("required", requiredParams, target, propertyKey || "");
};

// 方法装饰器:用来装饰类的方法
const validate: MethodDecorator = (
  target: Object,
  propertyKey: string | symbol,
  descriptor: PropertyDescriptor
) => {
  // 获取参数索引,从元数据中获取
  const requiredParams: number[] =
    Reflect.getMetadata("required", target, propertyKey || "") || [];
  const method = descriptor.value;
  descriptor.value = function (...args: any[]) {
    requiredParams.forEach((index) => {
      // 当传递的参数为空时,抛出异常
      if (args[index] === undefined) {
        throw new Error("参数不能为空");
      }
    });
    // 否则的话,正常执行函数,
    return method.apply(target, args);
  };
  return descriptor;
};

class User {
  @validate
  getUser(@requireDecorator id: number) {
    console.log("-------------");
    console.log(id);
    console.log(this.getUser);
    return id;
  }
}

const user = new User().getUser(5);
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值