如何创建 JavaScript 装饰器以及使用自动访问器如何帮助改善开发人员体验的演练。
GitHub 上的装饰器提案已经在分解装饰器的基本用例方面做得很好。我的目标不是在那里重新创建这些示例,而是突出一些鲜为人知的功能和交互。此外,在本系列的下一篇文章中,我将重点介绍如何在单个类属性上组合或链接多个装饰器。
每个代码示例都将附带一个交互式 Babel REPL 游乐场的链接,因此您可以自己尝试,而无需设置 polyfill 或启动存储库。在我的所有示例中,应选中左上角(“设置”下)的“评估”选项,这意味着您将能够查看代码、编辑它、打开浏览器的开发控制台,并在那里查看日志/结果。
你不需要关注 Babel REPL 右侧的转译代码,除非你想深入研究装饰器的 polyfill。 Babel REPL 的左侧是您可以编辑和编写代码来亲自尝试的地方。
要强调的是,您的开发人员工具的控制台应该显示控制台日志。如果没有,请确保在左上角选中“评估”。
装饰器规范的一个重要功能是自动访问器。我们将从学习它们是什么以及使用自动访问器如何使编写装饰器变得更容易开始。
装饰器提案在此处概述了自动访问器。但最终它只是一个简单的功能;让我们看一个基本的工作示例:Babel REPL。
class MyClass { accessor myBoolean = false }
在此类定义中,访问器关键字位于属性名称之前。然而,这并没有真正改变属性的任何内容 - 接下来,我们将看到自动访问器与装饰器结合使用时有多么有用。
(注意,您还可以将 static 与自动访问器一起使用,例如静态访问器 myBoolean = false)
为了更好地理解为什么我们使用自动访问器,让我们构建一些装饰器。
我们首先将自动访问器与实际上作用不大的装饰器结合起来,以便了解语法。
这是一个保留内部变量的函数装饰器,并允许您通过类上的属性获取和设置该变量:Babel REPL
function simpleDecorator(value, context) { let internalValue = false return { get() { return internalValue }, set(val) { internalValue = val return internalValue } } } class MyClass { @simpleDecorator accessor myBoolean }
这个装饰器返回一个对象,有两个方法:get()和set()。这就是自动访问器的装饰器如何“装饰”或将属性的 setter 和 getter 包装在一个地方;我们不必创建 simpleGetterDecorator 和 simpleSetterDecorator。相反,我们使用自动访问器将它们组合成一个定义,这更容易。
最后,到目前为止,这看起来是一个相当正常的函数 - 这非常适合介绍!
为了让我们为本文的其余部分做好准备,让我们更新我们的装饰器,以便它实际上执行某种验证。我们将制作一个装饰器,它只允许您设置偶数,而不允许设置其他数字。看起来像这样:Babel REPL
function onlyEvenNumbers(value, context) { let internalNumber = 0 return { get() { return internalNumber }, set(val) { const num = Number(val) if(isNaN(num)) { // don't set the value if it's not a number or coerced to a number return internalNumber } if(num % 2 !== 0) { // don't allow odd numbers return internalNumber } internalNumber = val return internalNumber } } } class MyClass { @onlyEvenNumbers accessor myEvenNumber }
因此,我们向 set() 方法添加逻辑,现在任何尝试在我们的类上设置 myEvenNumber 属性的人都将经历该验证逻辑。好的。
现在我们有了一个很好的仅偶数装饰器,让我们让它处理偶数和奇数,并提供一个选项来配置我们想要的数字类型!
幸运的是,因为我们在这里编写的是看起来相当正常的 JavaScript,所以将其配置为以这种方式工作并不难。我们用一个接受选项的函数包装原始装饰器,然后返回装饰器。巴别塔 REPL
function evensOrOdds(onlyEvens = true) { return function decorator(value, context) { let internalNumber = 0 return { get() { return internalNumber }, set(val) { const num = Number(val) if(isNaN(num)) { // don't set the value if it's not a number return internalNumber } if(num % 2 !== (onlyEvens ? 0 : 1)) { return internalNumber } internalNumber = val return internalNumber } } } } class MyClass { @evensOrOdds(true) accessor myEvenNumber @evensOrOdds(false) accessor myOddNumber }
我们现在已经将装饰器配置为接受任意选项,这允许装饰器的用户自定义其行为。耶。
装饰器可以使用的另一个工具是 context.metadata。该对象被传递给每个装饰器,您可以将其用于各种用途,但您需要小心,因为元数据对象对于每个装饰器的所有调用都是相同的。
继续阅读本系列的下一篇文章,了解如何将装饰器组合(或应用多个)到单个属性!
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3