早读《Understanding JavaScript Decorators》

  • 2019 年 12 月 24 日
  • 笔记

Understanding JavaScript Decorators

这是一篇讲 JavaScript 装饰器的文章,目前处于tc39第二阶段,由于是提取精髓,因此略有删减。

装饰器的收益在于可以抽象一部分通用的代码,在共享逻辑上,非常有用,让我们来看一个对属性设置只读的例子:

function readOnly(target, key, descriptor) {    return {      ...descriptor,      writable: false,    };  }    class Oatmeal extends Porridge {    @readOnly viscosity = 20;    // (you can also put @readOnly on the line above the property)      constructor(flavor) {      super();      this.flavor = flavor;    }  }

我们可以理解装饰器函数传递了三个参数给你使用,分别是 target ,key,descriptor,并且最后需要返回元素或类的属性描述符。

这个属性描述符相信你使用 defineProperty 时肯定用过,是的,就是定一个对象的行为描述。当你在装饰属性或方法时,你可以想象一下,你在操作具体的对象,最后返回一个描述符集合,系统帮助我们完成最后的设定。

function apiRequest(target, key, descriptor) {    const apiAction = async function(...args) {      // More about this line shortly:      const original = descriptor.value || descriptor.initializer.call(this);        this.setNetworkStatus('loading');        try {        const result = await original(...args);        return result;      } catch (e) {        this.setApiError(e);      } finally {        this.setNetworkStatus('idle');      }    };      return {      ...descriptor,      value: apiAction,      initializer: undefined,    };  }    class WidgetStore {    @apiRequest    async getWidget(id) {      const { widget } = await api.getWidget(id);      this.addWidget(widget);      return widget;    }  }

这个 original 就是外部 getWidget 方法。

最后我们再来看一看装饰类,实际上我们只需要传递第一个参数:

function customElement(name) {    return function(target) {      customElements.define(name, target);    };  }    @customElement('intro-message');  class IntroMessage extends HTMLElement {    constructor() {      super();    }  }

最后结论:

https://github.com/tc39/proposal-decorators

目前从公开的文档上来看,设计小组正准备将它重新设计为静态装饰器,未来有没有变动,暂时未知。目前 TypeScript 和 Babel 都对装饰器有支持,TS需要手动开启这个功能。

业务上装饰器用的恰到好处,确实能共享很多代码,但早期采用有一定的成本。