Decorator

1. 什么是装饰器

Decorator(装饰器),从名字上很容易让我们联想到装饰者模式。

简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。

ES6 中 Decorator 功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法。

Decorator 的两大优点:

  • 装饰器是典型的AOP(面向切面)思想,能把与核心业务无关的通用逻辑抽离出来。
  • 可以动态扩展对象的能力,且不影响同类的其他实例。

2. 装饰器使用

Decorator 修饰对象有两种:类和类属性 / 方法。

类的装饰

当对类本身进行装饰时,接收一个参数(类本身):

@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

类属性 / 方法的装饰

当对类属性或方法进行装饰时,能接收三个参数:target(类的原型)、propertyKey(属性名)、descriptor(属性描述符)。

多个装饰器

如果一个方法有多个装饰器,就像洋葱一样:先从外到内进入,再由内到外执行。

function dec(id) {
  console.log('evaluated', id);
  return (target, property, descriptor) => console.log('executed', id);
}

class Example {
  @dec(1)
  @dec(2)
  method() {}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1

外层装饰器 @dec(1) 先进入,但是内层装饰器 @dec(2) 先执行。

注意:装饰器不能用于修饰普通函数,因为函数存在变量声明情况,会产生歧义。

3. 使用场景

将 mixins 写成装饰器,让使用更简洁:

function mixins(...list) {
  return function (target) {
    Object.assign(target.prototype, ...list);
  };
}

const Foo = {
  foo() { console.log('foo'); }
};

@mixins(Foo)
class MyClass {}

const obj = new MyClass();
obj.foo(); // "foo"

4. core-decorators.js 常用装饰器

@autobind

autobind 装饰器使得方法中的 this 绑定到原始对象(避免回调中 this 丢失):

import { autobind } from 'core-decorators';

class Person {
  @autobind
  getPerson() { return this; }
}

const person = new Person();
const getPerson = person.getPerson;
getPerson() === person; // true

补充:@readonly、@deprecate、@nonconfigurable、@enumerable 等也来自 core-decorators,分别用于只读、废弃提醒、不可配置等场景。需要 @babel/plugin-proposal-decorators(legacy 模式)才能编译。

来源整理自:我的有道云笔记