Angular中的装饰器Decorator

646 阅读2分钟

在使用Angular中常常遇到装饰器,如下:

@NgModule({
  declarations: [
    AppComponent,
    HighlightDireactive
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
---------------------------------------
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

首先,什么是装饰器?

装饰器是一种函数,用于增强class的功能。写成@ + 函数名形式,如@NgModule,可以用来装饰四种类型的值。

  • 类的属性
  • 类的方法
  • 属性存取器(accessor)

在TS中这么定义装饰器

type Decorator = (
    value: Input,
    context: {
        kind: string;
        name: string | symbol;
        access: {
            get?(): unknown;
            set?(value: unknown): void;
        };
        private?: boolean;
        static?: boolean;
        addInitializer?(initializer: () => void): void;
    }) => Output | void;

包括两个参数,value和context。

  • value:所要装饰的值,某些情况下可能是undefined(装饰属性时)。
  • context:上下文信息对象。

@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true
  • testable装饰器作用于类MyTestableClass,给类加了静态属性isTestable。

装饰器的行为可以表示为如下形式,A = decorator(A) || A;decorator(A)为类A进行装饰。

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

装饰器在对类进行装饰的过程中,可以进行传参,因为装饰器就是一个函数

function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

由此可以类比Angular中的装饰器。

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'my-app';
}

其中,@Component中的对象是对AppComponent类进行装饰的辅助工具。

装饰器不仅可以装饰类,还可以装饰类的属性。

在Angular如下体现

export class AppComponent {
  @Input() inputValue:string;
  @ViewChild("childDom") childDom:ElementRef;
  title = 'my-app';
}

在类声明时,我们想为类的实例做装饰,此时类的实例还没有生成。依照JS的特性,此时我们可以在类的prototype对象上绑定属性和方法,再通过原型链实现实例拥有该类的某些“装饰属性”。如下readOnly装饰器

function readonly(target, name, descriptor){
  descriptor.writable = false;
  return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);

详情看阮一峰博客