资讯专栏INFORMATION COLUMN

【翻译】babel对TC39装饰器草案的实现

Mr_zhang / 3133人阅读

摘要:历史装饰器这个概念三年多前被首次提出。在版本年中发布了对装饰器的支持以及许多特性。有不同的安全级别需要考虑装饰器不应该意外泄漏私有名称。你可以测试装饰器的新语法,并向提案的作者们提出反馈意见。

原文地址:https://babeljs.io/blog/2018/...
原文作者:Nicolò Ribaudo

Babel 7.1.0最终支持新的装饰器提案,可以通过@babel/plugin-proposal-decorators插件使用。

历史
装饰器这个概念三年多前被Yehuda Katz首次提出。TypeScript在版本1.5(2015年)中发布了对装饰器的支持以及许多ES6特性。很多主流框架,像Angular和MobX,为了提高开发体验也开始使用装饰器。这些使装饰器变得很流行,并且给了社区一种很稳定的错觉。

Babel在版本5里面首次实现了装饰器,但在版本6的时候移除了,因为提案在不断的变化。Logan Smyth创建了一个非官方的插件(babel-plugin-transform-decorators-legacy)来代替Babel5里面的装饰器,在第一个Babel7 alpha版本发布的时候的时候,它被移到了Babel官方的存储库。这个插件还是使用老版本的插件语法,因为还不清楚新的提案会变成什么样。

从那个时候开始,Daniel Ehrenberg和Brain Terlson和Yehuda Katz一起成为了提案的作者,提案几乎完全被重写了。并非所有的事情都已经确定,而且目前也没有合规实施的方案。

Babel7.0.0为@babel/plugin-proposal-decorators插件介绍了一个新的标志:配置项legacy的唯一有效值为true。为了从提案的第一阶段平滑过渡到当前版本,需要有这种重大的改变。

在Babel7.1.0,我们引入了对这个新提案的支持,并且在使用@babel/plugin-proposal-decorators插件的时候会默认启用。如果我们不在Babel7.0.0里引入配置项legacy为true的话,在默认情况下就不可能使用正确的语义(也就意味着配置项legacy的值为false)

新的提案还支持私有字段和方法上的装饰器。我们还没有在Babel中实现这个功能(对于每个类而言,你可以使用装饰器或者私有元素),但很快就会实现的。

新的提案的改变点
尽管新的提案看上去跟旧的很相似,但还是有一些重要的不同点。

语法

旧的提案允许任何有效的左侧表达式(文字、函数和类表达式,new表达式和函数调用,简单和计算属性访问)作为装饰器的主体:

class MyClass {
     @getDecorators().methods[name]
     foo() {}

     @decorator
     [bar]() {}
 }

这个语法有一个问题:[...]这个符号在装饰器里进行属性访问以及定义计算属性名字的时候也会被用到。为了消除这个歧义,新的提案值允许用点符号来进行属性访问(foo.bar),也可以在最后加上一个括号(foo.bar())。如果你需要更多复杂的表达式,你可以用括号括起来:

class MyClass{ 
     @decorator
     @dec(arg1, arg2)
     @namespace.decorator
     @(complex ? dec1 : dec2)
     method() {}
 }

对象装饰器
旧版本的提案允许出现除了类和类元素装饰器之外的对象成员装饰器:

const myObj = {
  @dec1 foo: 3,
  @dec2 bar() {},
};

由于跟当前对象的一些表达语法的不兼容性,在提案中被移除了。如果你在你的代码中使用了对象成员装饰器,继续关注因为它们可能会在后续提案中被引入。

函数装饰器的参数
新提案引入的第三个重要变化是关于传递给装饰器函数的参数。

在第一版提案中,类元素装饰器接受一个目标类(对象),一个变量,和一个属性描述符-类似于传递给Object.defineProperty的参数。类装饰器将目标构造函数作为唯一的参数。

新的提案的装饰器更强大一些:元素装饰器接受一个对象,该对象除了更改属性操作符之外,还允许更改变量值,位置(staticprototype或者own)以及元素的种类(fieldmethod)。他们还可以创建其他的属性并定义运行在类装饰器里的函数。
类装饰器接受一个包含每个类元素的描述符的对象,从而保证可以在创建类之前修改它们。

升级
由于这些不兼容性,不能在现有的装饰器上使用新的提案:这会让升级特别慢,因为现有的库(MobX,Angular等)不能再没有引入重大改变的情况下进行升级。为了解决这个问题,我们已经发布了一个实用程序包,它将装饰器包装在你的代码里。运行这个之后,你可以安全地修改你的Babel配置以使用新的提案。
你可以使用下面这行代码去升级文件:

npx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --write 

如果你的代码只在Node中运行,或者你用Webpack或Rollup打包你的代码,你可以使用外部依赖来避免在每个文件中都注入包装函数:

npm install --save decorators-compat npx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --external-helpers --write

开放问题
并非所有的事情都已经确定:装饰器是一个非常大的功能,而且要以最好的方式定义它们是非常复杂的。

导出类的装饰器应该放在哪里
这个问题在装饰器的提案里反复出现:装饰器应该在export这个关键字的前面还是后面?

export @decorator class MyClass {}

// or

@decorator
export class MyClass {}

根本问题是export关键字是否是类声明的一部分,还是只是一个“包装器”。如果是前一种情况,它应该放在装饰器的后面,因为装饰器出现在声明的开头;在第二种情况下,它应该在装饰器前面,因为装饰器是类装饰器的一部分。

如何让装饰器和私有元素安全地互动?
装饰器引起了重要的安全问题:如果可以装饰私有元素,那么私有名称(也可以称为私有属性的变量名)可能会被泄漏。有不同的安全级别需要考虑:
1) 装饰器不应该意外泄漏私有名称。恶意代码不应该以任何方式从其他装饰器中“窃取”私有名称。
2) 只有直接应用于私有元素的装饰器才能被视为可信任:类装饰器应该无法读写私有元素?
3) 硬私有(类字段提案的目标之一)意味着私有元素应该只能有类的内部访问:任何装饰器是否可以访问私有名称?装饰器只能装饰公共元素么?
这些问题需要进一步讨论才能解决,这也是Babel的用武之地。

Babel的作用
随着What"s Happening With the Pipeline(|>) Proposal?这篇文章里的趋势,随着Babel7的发布,我们开始利用我们在JS生态系统中的位置,通过让开发人员测试和反馈有关提案的不同版本的体验来帮助提案的提出者们。
由于这个原因,在@babel/plugin-proposal-decorators更新的同时,我们也引入了一个新的属性:decoratorsBeforeExport,允许用户同时使用export @decorator class C {}@decorator export default class
我们也将引入一个属性来自定义私有属性装饰器的隐私约束。在TC39人员做出决定之前,这些属性是必需的,这样我们可以让默认行为成为最终提案知道的内容。
如果你直接使用我们的解析器(@babel/parse,以前的babylon),你已经可以在版本7.0.0里使用decoratorsBeforeExport属性:

const ast = babylon.parse(code, {
  plugins: [
    ["decorators", { decoratorsBeforeExport: true }]
  ]
})

用法
Babel用法:
shell版本:

npm install @babel/plugin-proposal-decorators --save-dev

JSON版本:

{
  "plugins": ["@babel/plugin-proposal-decorators", {"decoratorsBeforeExport": true }]
}

查看@babel/plugin-proposal-decorators文档了解更多属性。

你的作用
作为JavaScript的开发人员,你可以帮助概述该语言的未来。你可以测试装饰器的新语法,并向提案的作者们提出反馈意见。我们需要知道你在现实生活的项目中是怎么使用它们的。你也可以通过阅读问题中的讨论和proposal"s repository中的笔记中发现为什么要这样设计。
如果你想要立即尝试装饰器,你可以在我们的repl里使用不同的预设属性值。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102159.html

相关文章

  • 为何 ES Module 如此姗姗来迟

    摘要:最大的好处是对用户而言透明,可惜原因如前所述,此方案已否定。鉴于已经在正式提案中,倘若讨论持续僵持不下,不出意外将会随着时间推移而正式成为规范。月碰头会的与会者纷纷表示这次会议进展令人愉快,会议内容汇总在此,以及一些补充。 说明:本文发布之后,此问题的推进峰回路转,不停有新内容。文末新增一节 Updates,跟进本文发布之后的 ES Module 标准化进展情况。 浏览器大战多年了热度...

    xuexiangjys 评论0 收藏0
  • ES8 走马观花(ECMAScript2017 新特性)

    摘要:距离上一篇走马观花已经快两年时间了,上个月底正式发布,再写一篇姊妹篇,介绍新特性。会议的每一项决议必须大部分人赞同,并且没有人强烈反对才可以通过。已经准备就绪,该特性会出现在年度发布的规范之中。 距离上一篇《ES6 走马观花》已经快两年时间了,上个月底 ES8 正式发布,再写一篇姊妹篇,介绍 ES8 新特性。 什么是 ES8 ES8 是 ECMA-262 标准第 8 版的简称,从 ES...

    meislzhua 评论0 收藏0
  • 【译】关于转译 JavaScript 程序员需要知道

    摘要:他们的计划是,使用微软开发者们所习惯的其他语言的开发工具所支持的静态类型,得到更好的代码。在微软内部,被和以及团队所使用,而且它被系的等公司使用。标准的编辑,同时也是微软项目高级经理的也同意。 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/895原文:http://thenewstack.io/javascript-transpilers-n...

    freecode 评论0 收藏0
  • 前端背景知识查漏补缺

    摘要:提交内容可以是一个提议想法初步描述该阶段是对所提交新特性的正式建议。在这个阶段需具备以下条件指定一名成员作为审阅通过有实现的或者初步编写标准,包括问题描述解决方案示例语法语义关键的算法及抽象实现在的复杂度等该阶段是会出现标准中的第一个版本。 ECMAScript 与 JavaScript ECMAScript 是一套脚本语言的规范,内部编号 ECMA-262 该规范由 Ecma(Eu...

    developerworks 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<