前端面试题 - 51. 谈谈decorator

232 阅读3分钟

定义

装饰器模式(Decorator Pattern)是一种结构型设计模式,旨在促进代码复用,可以用于修改现有的系统,希望在系统中为对象添加额外的功能,同时又不需要大量修改原有的代码。

JS中的装饰器是ES7中的一个新语法,用来增强 JavaScript 类(class)的功能,可以对方法属性进行修饰。

function color(value: string) { // 这是一个装饰器工厂
    return function (target) { // 这是装饰器
        // do something with "target" and "value"...
    }
}

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。

eg:将类密封

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

eg:重载构造函数

function classDecorator<T extends {
    new(...args: any[]): {}
}>(Base: T) {
  // 注意这里的写法
  return class extends Base {
      newProperty = "new property";
      hello = "override";
  }
}
@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
      this.hello = m;
  }
}
console.log(new Greeter("world"));

image.png

方法装饰器

方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。

参数

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 成员的属性描述符
class Greeter {
  private greeting: string;
  constructor(m: string) {
    this.greeting = m
  }

  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}
const test = new Greeter("world")
console.log(test.greet());// Hello, world

访问器装饰器

访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义。

参数

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 成员的属性描述符
function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
      descriptor.configurable = value;
  };
}

属性装饰器

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

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

import "reflect-metadata";
const formatMetadataKey = Symbol("format");
function format(formatString: string) {
    return Reflect.metadata(formatMetadataKey, formatString);
}
function getFormat(target: any, propertyKey: string) {
    return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

class Greeter {
  @format("Hello, %s")
  greeting: string;
  constructor(message: string) {
      this.greeting = message;
  }
  greet() {
      let formatString = getFormat(this, "greeting");
      return formatString.replace("%s", this.greeting);
  }
}

参数装饰器

参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。

参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
  • 参数在函数参数列表中的索引。

加上配置

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            { "legacy": true }
        ]
    ]
},
import "reflect-metadata";
const requiredMetadataKey = Symbol("required");
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
    let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}
function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) {
    let method = descriptor.value;
    descriptor.value = function () {
        let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);
        if (requiredParameters) {
            for (let parameterIndex of requiredParameters) {
                if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {
                    throw new Error("Missing required argument.");
                }
            }
        }
        return method.apply(this, arguments);
    }
}

class Greeter {
  greeting: string;
  constructor(message: string) {
      this.greeting = message;
  }
  @validate
  greet(@required name: string) {
      return "Hello " + name + ", " + this.greeting;
  }
}

参考: TS装饰器的介绍: www.bookstack.cn/read/TypeSc… 常见的应用:www.imooc.com/read/72/art…