[译]探索 ECMAScript 装饰器

探索 ECMAScript 装饰器

迭代器(Iterators), 生成器(generators)数组简约式(array comprehensions);随着时间的推移,JavaScript 和 Python 愈来愈像,现在我已经见怪不怪了。今天咱们就来讨论一个相似 Python 语法的 ECMAScript 提议:装饰器,该提案来自 Yehuda Katz。javascript

更新 07/29/2015: 装饰器提议已经提交到TC39。最新进展你可在 提议 仓库找到。如今又出了几个 新的例子 (译注:TC39 全称 TC39 ECMA 技术委员会,受特许解决JavaScript语言相关事宜。)html

装饰器模式

到底什么是装饰器?在 Python中,装饰器提供了一个很是简单的语法,用于调用高阶函数。一个 Python 装饰器就是一个函数,它包装另一个函数来拓展功能,而不须要作显式的修改。最简单的 Python 装饰器看起来是这样的:前端

代码顶部(@mydecorator)就是一个装饰器,它看起来和 ES2016(ES7)没有什么区别,因此你必定分清楚!:)。java

@ 向编译器代表,咱们正在使用装饰器,mydecorator 指向一个同名的函数。咱们的装饰器接受一个参数(被装饰的函数),拓展功能后,返回一个与参数同名的函数。python

装饰器帮助你添加任何你想拓展的功能,好比 memoization(译者注:一种将函数返回值缓存起来的方法),强制访问控制,身份验证,插桩,时间函数,日志,比率限制,等等。react

在 ES5 和 ES2015(即ES6) 中的装饰器

在 ES5 中,实现命令式装饰器(做为纯函数)是至关麻烦的。在 ES2015(即ES6)中,当类支持扩展,咱们有多个类须要共享一个功能时,咱们就须要更好的方法;或者说须要更好的分配方法。android

Yehuda 的装饰器建议寻求在设计时对 JavaScript 类、属性和对象字面量进行注释和修改,同时保持声明式语法。ios

让咱们来看一些 ES2016 装饰器吧!git

ES2016 装饰器

想一想咱们在 Python 中学到的知识。一个 ES2016 装饰器是一个表达式,它返回一个函数以及接收一个目标体,名称,属性描述符来做为参数。经过在装饰器前面加一个 @ 符号,而后放到被装饰者的最上面来使用装饰器。装饰器能够被定义为类或者属性。es6

装饰一个属性

咱们来看一个基础的 Cat 类:

编译这个类的结果就是将 meow 函数加载到 Cat.prototype,大体以下:

设想一下,咱们但愿标记一个属性或者方法名不能被编辑。装饰器优先级高于定义属性的语法,所以咱们能够定义一个 @readonly 装饰器,以下:

咱们就能够这样来定义 meow 属性了,以下:

装饰器就是一个表达式,它会被执行而后返回一个函数。这就是为何 @readonly@something(parameter) 都能工做。

在 描述符(descriptor)加载进 Cat.prototype 以前,JavaScript引擎会先调用装饰器:

如今 meow 变成了只读,咱们能够来验证一下:

不只仅是属性,接下来咱们来探讨装饰器类,在此以前咱们先来看第三方库,尽管都很年轻,装饰器库从2016年开始陆续出现,包括由 Jay Phelps 开发的 decorators.js

和咱们上面实现的 readonly 同样,decorators.js 包含了 @readonly , 只须要导入就好了:

它还包含其余的装饰器,好比 @deprecate ,主要是用于,当你的API须要提示方法可能会改变:

调用 console.warn() 打印描述信息。也能够自定义描述信息,也能够在描述信息中添加连接,以便进一步阅读。

装饰一个类

接下来咱们来看看装饰类。根据提议规范,一个装饰器接收构造函数做为参数。假设有一个 MySuperHero 类,咱们能够定义一个简单的装饰器 @superhero来装饰它:

这能够进一步拓展,经过提供参数使咱们可让装饰器定义成工厂函数:

ES2016 装饰器做用于描述符和类。它们会自动接收被传递的属性名和目标对象,咱们很快会讲到。经过对描述符的访问,装饰器能够作到更改属性使其使用 getter,或开启一些本来很是繁琐的操做,好比在第一次访问属性时自动绑定方法到当前实例。

ES2016 装饰器 和 Mixins 模式

我拜读了 Reg Braithwaite 最近的文章 ES2016 Decorators as mixins 和以前的一篇 Functional Mixins。Reg 提出使用一个 helper 将不一样行为混入任意一个目标(类原型或者 standalone),并表述为一个类专属的版本。这种功能性的混入会把实例行为混入类原型,使其看起来像这样:

好了,咱们如今能够定义一些 mixins ,而后尝试用它们装饰一个类。假设咱们有一个简单的 ComicBookCharacter 类:

ComicBookCharacter 多是世界上最无聊的角色了,可是咱们能够定义一些 mixins,为它提供超能力(SuperPowers)和 装备(UtilityBelt),让咱们用 Reg 的 mixin helper 来实现吧:

如今咱们就能够经过在 mix 函数前面加 @ 的语法,根据咱们想要的属性来装饰 ComicBookCharacter。注意咱们是如何在类上面加多个装饰器语句的:

如今咱们能够塑造一个蝙蝠侠角色了。

这些类的装饰器相对紧凑,我能够将它们用做函数调用的替代方法,或者做为高阶组件的助手。

注: @WebReflection 有一些替代方案,用于本节中使用的mixin模式,您能够点击 了解更多

经过 Babel 使用装饰器

装饰器(在我写本文的时候)仍然仍是一个提案。他们尚未经过。感谢 Babel 支持在实验模式使用装饰器语法,因此本文的大部分例子均可以直接使用。

若是你使用 Babel CLI,你能够出入以下参数:

$ babel --optional es7.decorators
复制代码

或者直接调用 transformer:

这里有一个 Babel 在线的 REPL;复选框选中“Experimental”就可使用装饰器了。

有趣的实验

我很幸运坐在 Paul Lewis 的旁边,他在尝试用装饰器从新调度读写 DOM 的代码。它借鉴了 Wilson Page 的 FastDOM,可是提供了更精简的API。Paul 的 read/write 装饰器能够经过 console 来提醒你,若是你在改变布局时使用 @write 后调用方法或者属性(或者使用 @read 后改变DOM)。

下面是 Paul 的一个实验例子,在使用 @read 后尝试改变 DOM,会在 console 中打印异常:

如今就去试试装饰器吧!

在短期来看,ES2016装饰器对于声明式装饰和注释,类型检查和在 ES2015 类中应用装饰器都是大有裨益的,从长远来看,他们能够提供很是有用的静态分析工具(编译时类型检查和自动补全)。

他们和经典面向对象语言(OOP)中的装饰器没有区别,容许一个对象能够被行为装饰,不论是动态的仍是静态的都不会影响到来自同一个类的对象。装饰器的提案还一直在变化中,让咱们持续关注 Yehuda 的提案吧。

第三方库的做者正在讨论,装饰器可能会替换 mixins,以及他们能够应用到 React 的高阶组件中。

我我的很但愿看到关于装饰器愈来愈多的尝试,你能够在 Babel 上尝试,识别出可复用的装饰器,也许你也能够像 Paul 那样分享你的做品 :)

了解更多以及参考

感谢 Jay PhelpsSebastian McKenziePaul LewisSurma 对本文的审校以及提供的详细反馈❤


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索