Angular装饰器详解(二) - 前言篇2

61 阅读1分钟

我们继续来看一下,类方法装饰器类属性装饰器类访问器装饰器

类方法装饰器

装饰器可以装饰类的方法,它会修改类的方法。

class C {
  
  @trace
  toString() {
    return 'C';
  }
}

// 相当于
C.prototype.toString = trace(C.prototype.toString);

方法装饰器会返回一个新的函数,取代原来的方法,也可以不返回值,表示依然使用原来的方法。如果返回其他类型的值,就会报错。也就是说,如果返回值的话,必须返回一个函数。

class Person { 
    constructor(name){
        this.name = name;
    }

    @MyName
    getName(){
        console.log(this.name);
        return this.name;
    }
}

function MyName(target, key, descriptor){
    // console.log(target, key, descriptor);
    const fn = descriptor.value;

    descriptor.value = function(...args){
        console.log(`${key} is called...` );

        // fn && fn(...args);
        fn && fn.apply(this, args);
    }
}

let person = new Person('Tom');
person.getName();

参数说明:

  • target: 类的原型对象,上例是Person.prototype;
  • key: 所要修饰的属性名 name;
  • descriptor: 该属性的描述对象;

装饰器第一个参数是类的原型对象,上例是Person.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);第二个参数是所要装饰的属性名,第三个参数是该属性的描述对象。

类的属性

属性装饰器的类型描述如下。 装饰器可以装饰类属性。(新语法)

type ClassFieldDecorator = (value: undefined, context: { 
    kind: "field"; 
    name: string | symbol; 
    access: { get(): unknown, set(value: unknown): void }; 
    static: boolean; 
    private: boolean; 
}) => (initialValue: unknown) => unknown | void;
function logged(value, { kind, name }) {
  if (kind === "field") {
    return function (initialValue) {
      console.log(`initializing ${name} with value ${initialValue}`);
      return initialValue;
    };
  }

  // ...
}

class C {
  @logged x = 1;
}

new C();
// initializing x with value 1

用户可以选择让装饰器返回一个初始化函数,当该属性被赋值时,这个初始化函数会自动运行,它会收到属性的初始值,然后返回一个新的初始值。属性装饰器也可以不返回任何值。

如果不使用装饰器语法,则如下所写。

let initializeX = logged(undefined, {
  kind: "field",
  name: "x",
  static: false,
  private: false,
}) ?? (initialValue) => initialValue;

class C {
  x = initializeX.call(this, 1);
}

访问器

类装饰器引入了一个新命令accessor,用来属性的前缀。(新语法)

class C {
  accessor x = 1;
}

它是一种简写形式,相当于声明属性 x 是私有属性 #x 的存取接口。上面的代码等同于下面的代码。

class C {
  #x = 1;

  get x() {
    return this.#x;
  }

  set x(val) {
    this.#x = val;
  }
}

accessor 命令前面还可以接受属性装饰器,这里不在举例了。

好了,下次介绍多装饰器的情况。