早讀《Understanding JavaScript Decorators》
- 2019 年 12 月 24 日
- 筆記
這是一篇講 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需要手動開啟這個功能。
業務上裝飾器用的恰到好處,確實能共享很多程式碼,但早期採用有一定的成本。