浅谈js装饰器之成员装饰器

102 阅读3分钟

前言

继上篇文章后,继续对装饰器进行学习,本文作为学习记录以及学习分享。下面代码中所有的any类型只是不想让demo在ts中出现报错。在实际开发中都可根据实际情况来进行替换。

什么是成员装饰器?

指的是类的成员的装饰器。类成员一共分为两类:属性方法。下面来分享一下成员装饰器的具体用法。

属性装饰器

属性装饰器是一个函数,该函数接受两个参数。

第一个参数需要根据具体情况来决定。

  1. 如果是实例属性,第一个参数为类的原型(prototype)。
  2. 如果是静态属性,第一个参数为类本身

第二个参数固定为一个字符串,表示属性名

既然知道了属性装饰器的本质,那就尝试书写一下:

实例属性装饰器

type decoratorType = new (...args: any) => object;

function funcDecorator(target: any, key: string) {
  console.log(target === A.prototype, key);
  if (!target.__props) {
    target.__props = [];
  }
  target.__props.push(key);
}

class A {
  @funcDecorator
  prop1: string;
  
  @funcDecorator
  prop2: string;
}

console.log((A.prototype as any).__props);

以上代码为两个类的实例属性添加了装饰器。所以自然而然属性装饰器会运行两次。让我们来看一下运行结果吧~

image.png 因为被装饰的是实例属性,所以装饰器的第一个参数为类的原型。这从运行结果中可以验证到。那既然能获取到原型,就可以根据实际情况书写多种逻辑了。

静态属性装饰器

function funcDecorator(target: any, key: string) {
  console.log(target, key);
}

class A {
  @funcDecorator
  static prop2: string;
}

当修饰类的静态属性时,装饰器第一个参数获取到的是类本身。以下为运行结果:

image.png

当然,属性装饰器也可以使用装饰器工厂的形式来书写,用来接受参数参与逻辑。

function funcDecorator(prop: any) {
  return function (target: any, key: string) {
    console.log(target, key);
  };
}

class A {
  @funcDecorator("我是属性装饰器")
  static prop2: string;
}

方法装饰器

方法装饰器当然也是一个函数,该函数接受三个参数。

第一个参数也是需要根据具体情况来决定。

  1. 如果是实例方法,第一个参数为类的原型(prototype)。
  2. 如果是静态方法,第一个参数为类本身

第二个参数固定为一个字符串,表示方法名

第三个参数为属性描述对象。如果忘记了的话可以看一下这里

function funcDecorator(prop: any) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    console.log(target, key, descriptor);
  };
}

class A {
  @funcDecorator("我是属性装饰器")
  method1() {}
}

运行后输出结果为:

image.png 对于方法装饰器来说,可以通过属性描述对象来对这个方法做一些操作,比如是否可被遍历,设置getter/setter等等。

比如我想书写两个方法装饰器eumerableuseless。分别表示该方法可被遍历和提示该方法已经被启用。那就可以这么写:

function enumerable(target: any, key: string, descriptor: PropertyDescriptor) {
  descriptor.enumerable = true;
}

function useless(target: any, key: string, descriptor: PropertyDescriptor) {
  descriptor.value = function () {
    console.warn(`${key}方法已过期!`);
  };
}

class A {
  @enumerable
  method1() {}

  @useless
  @enumerable
  method2() {}
}
const a = new A();
for (const i in a) {
  console.log(i);
}
a.method2();

image.png

个人认为装饰器对于通用逻辑定制化逻辑都非常适用。

结尾

今天的学习和分享就到这了,如果不足欢迎指出!祝大家工作顺利,永无bug~