返回

让 JS 装饰器随心所欲——core-decorators(decorate)

前端

这次要分析的源代码是 core-decorator 库中的一个函数:decorate。先简单介绍一下 core-decorators这个库是干嘛的, core-decorators是一个按照 JavaScript state-0 decorators 提案编写的一个装饰器库。

装饰器作为 ES7 提案之一,目的是让 JavaScript 的装饰器实现随心所欲,随心所欲到什么程度呢,就是你可以自己编写自己的装饰器。而且装饰器的使用范围非常广,类、方法、属性、参数都可以装饰。这么强大的装饰器,如今我们却用不到吗?事实上,ES7 虽然还没最终敲定,但是装饰器提案已经得到了广泛支持,各大浏览器也已经开始支持装饰器,各大框架也开始利用装饰器做文章。

在这里举一个装饰器可以做什么事情的例子。众所周知,在 ES7 之前,想要给函数增加一个计时功能是比较繁琐的,必须手写一堆代码。有了装饰器之后,只需要一行代码就可以搞定。

@timing
function foo() {
  // do sth.
}

如此一来,每当调用 foo 函数,计时器便会启动。需要注意的是,这里使用的是我自己定义的 @timing 装饰器,而这个装饰器是如何实现的呢?这就需要用到 core-decorators。

core-decorators 想要完成自定义装饰器的功能,必须提供一个方法让我们去自定义装饰器,而这个方法就是 decorate。要弄清楚 decorate 这个方法是做什么的,可以看看它的API,其定义如下:

decorate(originalDescriptorOrClass, decoratorDescriptors)

从 API 可以看出,decorate 方法需要接收两个参数。第一个参数 originalDescriptorOrClass 可以是类符或者方法符。因为装饰器可以作用于类和方法,所以这个参数的类型就是“类描述符或者方法描述符”。第二个参数 decoratorDescriptors 就是对类或者方法进行修饰的描述符数组,它也是装饰器发挥作用的关键。

现在来简单分析下 decorate 是如何工作的。首先,判断第一个参数是否为类描述符,如果是类描述符,那么就对类的属性和方法进行装饰;如果不是类描述符,那么就对方法进行装饰。对方法进行装饰也分为两种情况,一种是修饰方法本身,另一种是修饰方法的属性,最后把修饰后的描述符返回出去。

到这里,我们就对装饰器以及 decorate 方法有了大致的了解了,接下来我们来看下 decorate 方法是怎么实现的。

export function decorate(originalDescriptorOrClass, decoratorDescriptors) {
  // 判断是否是类
  const classDescriptor = typeof originalDescriptorOrClass === 'function';

  if (classDescriptor) {
    // 如果是类,修饰其原型上的方法和属性
    return classDecoratorFactory(originalDescriptorOrClass, decoratorDescriptors);
  }

  // 如果是方法,则可能需要修饰方法本身,或者方法的属性
  const {writable, configurable} = originalDescriptorOrClass;
  // 1. 修饰方法本身
  if (writable || configurable) {
    return methodDecoratorFactory(originalDescriptorOrClass, decoratorDescriptors);
  }

  // 2. 修饰方法的属性
  return accessorDecoratorFactory(originalDescriptorOrClass, decoratorDescriptors);
}

装饰器的原理其实很简单,就是通过修改描述符来达到修饰类或方法的目的。core-decorators库的decorate方法也是如此,它根据不同的情况对描述符进行修改,从而实现装饰器的功能。