探索 EcmaScript 装饰器(译)
最近在写一篇关于装饰器和箭头函数的文章,最终局限于个人对装饰器认知有限,无法更加全面的解释和理解相关内容,所以找到了 Google-Developers 这个组织里的一篇文章,收获颇深。
——译者注
文档源地址
装饰器模式
到底什么是装饰器呢?在 Python 中,装饰器为高阶函数的调用提供了很简单的语法。一个 Python 装饰器是一个可以携带另外一个函数的函数,以不修改原有函数的前提下继承这个函数。Python 中最简单的装饰器看起来如下:

最顶部的(
@mydecorator
)就是装饰器而且要注意的是看起来就和 ES2016 里的没什么区别。
@
符号告诉解释器我们在使用一个引用叫 mydecorator 函数的装饰器。我的装饰器以被装饰的函数作为参数并且返回了被添加了外功能的函数。
装饰器对任何你想要非常透明的给添加额外功能的东西都很有帮助。包括备忘录模式,强制控制可访问以及权限认证,工具类以及时间相关的函数, 日志,速度限制等等。
ES5 和 ES2015(别称 ES6)中的装饰器
在 ES5 中,实现一个装饰器(作为一个纯函数)是很无聊的。而在 ES2015 中,类支持继承的时,当我们有多个类都需要去实现某种功能时装饰器就变成了一种更好的分布方法。
Yehuda 的装饰器尝试去支持注解并且修改 JavaScript 类,原型以及在定义时保持一种声明式的语法。
我们再来看看 ES2016 的装饰器
请记住我们从 Python 中学到的东西。ES2016 的装饰器就是一个返回了一个可以接收 target,name 和 属性描述作为参数的方法。你可以添加@
为前缀并放到你想修饰的任何地方的顶部。装饰器可以被定义到一个类或者属性上。
修饰一个属性
我们来看看一个基类 Cat

给
Cat.prototype
上添加 meow 函数大致如下:
想象一下,我们想要标记一个属性或者方法为不可写的。只需要在定义属性的时候在定义语法前写上一个装饰器。我们可以这样定义一个
@readonbly
装饰器:
然后把它添加到我们的 meow 方法上:

装饰器就是一个能够取值输入并返回一个函数的表达式。这也就是为什么 @readonly
和 @something(parameter)
都可以的原因。
现在,在把 descriptor 装载到 Cat 的原型上之前,引擎会先调用装饰器:

最终呢,这个 meow 方法会变成只读的。我们现在来通过以下方式来确认一下:

再好不过了,不是吗?
-----------------------------------------略
装饰一个类
接下来我们来看看装饰一个类。在这个例子中,对于一个虚拟的父类 MySuperHero
,我们可以定义一个简单的装饰器 @superhero
:

这样还能被拓展更多,让我们能够在定义装饰器函数的时候注入更多参数:

ES2016 装饰器依赖于属性 descriptors 和类本身。它们可以自动拿到传递过来的属性名称和 target 对象,我们等一下细说。能够操作 descriptor 使得我们能够做一些使用 getter 修改一个属性这样的事情,还能够避免绑定一个方法到当前实例上这样笨重的操作。
ES2016 装饰器和 Mixins
我非常享受阅读 Reg Braithwaite 最近的文章 ES2016 装饰器和 mixins 以及上个系列 功能性的 Mixins。Reg 提出了一个能够将一些行为函数加入仁和 target (类的原型或者其他)工具类并且能够描述类的特定版本。功能性的 mixin 能够将一些实例的行为函数注入类的原型,如下:

好,我们现在需要定义一些 mixins 并且尝试使用它们装饰一个类。我们想想我们有一个简单的类
ComicBookCharacter
类:
这可能是世界上最无聊的角色,但我们可以定义一些可以提供像
超能量
和 秘密武器
这样功能的 mixins:
有了这些东西,我们就可以使用
@
语法搭配我们的 minxs 的名字装饰 ComicBookCharactor
来获取我们所期望的功能。注意我们是使用以下方式来为类使用多个装饰器的:
现在,让我们来使用我们所定义的东西来创建一个蝙蝠侠:

这些类的装饰器都是相当简洁的并且我可以认为我是在调用一个方法或者是高阶组件的一种辅助工具。
通过 Babel 开启装饰器功能
装饰器迄今仍只是个提案,它仍没有被正式通过。据说,多亏了 Babel 能够支持一些试验性的语法的转译,因此大部分语法特性都可以通过它来实际应用。
在使用 Babel 命令行工具的时候,你可以选择执行一下选项:
$ babel --optional es7.decorators
或者你也可以选择以下方式:

这是在线文档;点击 Experimental
复选框并尝试它!