跳至主要內容

core-js@3, Babel 和展望未来

大约 24 分钟blog

core-js@3, Babel 和展望未来

经过一年半的开发,数十个版本,许多不眠之夜,core-js@3open in new window 终于发布了。这是 core-jsbabelopen in new window 补丁相关的功能的最大的一次变化。

什么是 core-js?

它是最普遍、最流行open in new window 的给 JavaScript 标准库打补丁的方式,但是有很大一部分开发者并不知道他们间接的使用了core-js🙂

贡献

core-js 是我自己爱好的项目,没有给我带来任何利润。它花了我很长的时间,真的很昂贵:为了完成 core-js@3,我在几个月之前已经离开我的工作。这个项目对许多人和公司起到了促进作用。因为这些,筹集资金去支持 core-js 的维护是说得通的。

如果你对 core-js 感兴趣或者在你每天的工作中有使用到,你可以在 Open Collectiveopen in new window 或者 Patreonopen in new window 成为赞助者。

你可以给open in new window提供一个好的工作,和我现在做的相关的。

或者你可以以另一种方式贡献,你可以帮助去改进代码、测试或者文档(现在,core-js 的文档还很糟糕!)。

core-js@3 有哪些变化?

JavaScript 标准库中变化的内容

由于以下两个原因,这个版本包含丰富的、新的 JavaScript 补丁:

  • core-js 只在 major(主)版本更新时才有 break changes,即使需要和提案的内容对齐。
  • core-js@2 在一年半前已经进入功能冻结阶段了;所有新的功能只能够添加到 core-js@3 这个分支。

稳定的 ECMAScript 功能

稳定的 ECMAScript 功能在 core-js 中已经几乎完全支持有很长一段时间了,除此之外,core-js@3 引进了一些新功能:

一些在 ES2016-ES2019 中作为提案被接受且已经使用很长时间的功能,现在被标记为稳定:

修复了针对浏览器的许多问题,例如,Safari 12.0 Array.prototype.reverse bugopen in new window 已经被修复了。

ECMAScript 提案

除了上文提到的支持内容,core-js@3 现在还支持下面的 ECMAScript 提案:

一些提案的变化很大,core-js 也将相应的更新:

web 标准

许多有用的功能被添加到这个类别中。

最重要的一个是 URLopen in new windowURLSearchParamsopen in new window。他是最受欢迎的功能请求之一open in new window。增加 URLURLSearchParams,并保证他们最大限度的符合规范,保持源代码足够紧凑来支撑任何环境是 core-js@3 开发中最困难的任务之一open in new window

core-js@3 包函在 JavaScript 中创建微任务( microtask )的标准方法:queueMicrotaskopen in new windowcore-js@2 提供了 asap 函数,提供了同样功能的老的提案。queueMicrotask 被定义在 HTML 标准中,它已经能够在现代浏览器比如 Chromium 或者 NodeJS 中使用。

另一个受欢迎的功能请求是支持 DOM 集合的 .forEach 方法open in new window。由于 core-js 已经针对 DOM 集合迭代器做了 polyfill,为什么不给 节点列表DOMTokenListopen in new window 也增加 .forEach 呢?

移除过时的功能:

  • Reflect.enumerate 因为他已经从标准中移除了
  • System.globalglobal 现在他们已经被 globalThis 代替
  • Array.prototype.flatten 现在被 Array.prototype.flat 代替
  • asapqueueMicrotask 代替
  • Error.isError 被撤销很长时间了
  • RegExp.escape 很久之前被拒绝了
  • Map.prototype.toJSONSet.prototype.toJSON 也是很久前被拒绝了
  • 不必要并且被错误添加的迭代器方法:CSSRuleListMediaListStyleSheetList

不再有非标准、非提案的功能

许多年前,我开始写一个库,他是我的 JavaScript 程序的核心:这个库包函 polyfills 和一些我需要的工具函数。一段时间后,这个库以 core-js 命名发布。我认为现在大多数 core-js 用户不需要非标准的 core-js 功能,他们大多已经在早期版本移除了,现在是时候将剩余部分从 core-js 中移除。从这个版本开始,core-js 可以被称为 polyfill 了。

包、入口和模块名字

一个 issue 里提了 core-js 包的很大( ~2MB ),有很多重复文件。因为这个原因,core-js 分成了 3 个包:

core-js 的早期版本中,稳定的 ECMAScript 功能和 ECMAScript 提案的 polyfill 模块化需要分别加 es6.es7. 前缀。这是在 2014 年做的决定,那时将 ES6 之后的所有功能都视为 ES7。在 core-js@3 中所有稳定的 ECMAScript 功能都增加 es. 前缀,ECMAScript 提案增加 esnext. 前缀。

几乎所有的 CommonJS 入口都改变了。core-js@3 相比于 core-js@2 有更多的入口:这带来的最大限度的灵活性,使你能够仅仅引入你的应用需要的依赖。

这里是一些例子关于如何使用新的入口:

// 使用 `core-js` 全部功能打补丁:
import "core-js";
// 仅仅使用稳定的 `core-js` 功能 - ES 和 web 标准:
import "core-js/stable";
// 仅仅使用稳定的 ES 功能
import "core-js/es";

// 如果你想用 `Set` 的补丁
// 所有 `Set`- ES 提案中,相关的功能:
import "core-js/features/set";
// 稳定的 `Set` ES 功能和来自web标准的功能
// (DOM 集合迭代器)
import "core-js/stable/set";
// 只有 `Set` 所需的稳定的 ES 功能
import "core-js/es/set";
// 与上面一致,但不会污染全局命名空间
import Set from "core-js-pure/features/set";
import Set from "core-js-pure/stable/set";
import Set from "core-js-pure/es/set";

// 仅仅为需要的方法打补丁
import "core-js/feature/set/intersection";
import "core-js/stable/queque-microtask";
import "core-js/es/array/from";

// 为 reflect metadata 提案打补丁
import "core-js/proposals/reflect-metadata";
// 为所有 stage 2+ 的提案打补丁
import "core-js/stage/2";

其他重要的变化

core-js polyfill 能够 配置侵入等级open in new window。如果你认为有些情境 core-js 功能检测侵入性太强,原生实现对你来说已经足够,或者一个错误的实现没有被 core-js 检测到,你可以修改 core-js 的默认行为。

如果无法安装规范的每个细节实现某个功能,core-js 增加了一个 .sham 属性,例如,IE11 中 Symbol.shamtrue

不再有 LiveScript! 当我开始写 core-js 时,我主要使用的是 LiveScriptopen in new window ;一段时间后,我用 JavaScript 重写了全部的 polyfills 。在 core-js@2 中测试和帮助的工具函数仍然使用 LiveScript :它是非常有趣的像 CoffeeScript 一样的语言,有强大的语法糖使你能够写非常紧凑的代码,但是它几乎已经死了。除此之外,它也是为 core-js 贡献的屏障,因为大多数 core-js 用户不知道这个语言。core-js@3 测试和工具函数使用现代 ES 语法:它将成为为 core-js 贡献的好时机 🙂。

对于大多数用户,为了优化 core-js 导入,我建议使用 babel。当然,有些情况下 core-js-builderopen in new window 仍然有用。现在它支持 target 参数,使用带有目标引擎的browserslistopen in new window 查询 - 你能够创建一个 bundle,仅仅包含目标引擎需要的 polyfills。对于这种情况,我做了 core-js-compatopen in new window,更多关于它的信息,你能够从 这篇文章的 @babel/preset-env 部分了解到。


这仅仅是冰山一角,更多的变化在内部。更多关于 core-js 变化可以在 changelogopen in new window 中找到。

Babel

正如上文提到的,babelcore-js 是紧密集成的:babel 提供了优化 core-js 优化导入的可能性。core-js@3 开发中很重要的一部分是改进 core-js 相关的 babel 功能(看这个 PRopen in new window)。这些变化在 Babel 7.4.0open in new window 发布了。

babel/polyfill

@babel/polyfillopen in new window 是一个包裹的包,里面仅仅包含 core-js 稳定版的引入(在 Babel 6 中也包含提案)和 regenerator-runtime/runtime,用来转译 generators 和 async 函数。这个包没有提供从 core-js@2core-js@3 平滑升级路径:因为这个原因,决定弃用 @babel/polyfill 代之以分别引入需要的 core-jsregenerator-runtime

原来

import "@babel/polyfill";

现在使用两行代替:

import "core-js/stable";
import "regenerator-runtime/runtime";

别忘记直接安装这两个依赖!

npm i --save core-js regenerator-runtime

@babe/preset-env

@babel/preset-envopen in new window 有两种不同的模式,通过 useBuiltIns 选项:entryusage 优化 core-js的导入。

Babel 7.4.0 引入了两种模式的共同更改,以及每种模式的特定的修改。

由于现在 @babel/preset-env 支持 core-js@2core-js@3,因此 useBuiltIns 需要新的选项 -- corejs,这个选项用来定义使用 core-js 的版本(corejs: 2 或者 corejs: 3)。如果没有设置,corejs: 2 是默认值并且会有警告提示。

为了使 babel 支持将来的次要版本中引入的 core-js 的新功能,你可以在项目中定义明确的次要版本号。例如,你想使用 core-js@3.1 使用这个版本的新特性,你可以设置 corejs 选项为 3.1corejs: '3.1' 或者 corejs: {version: '3.1'}

@babel/preset-env 最重要的一个功能就是提供不同浏览器支持特性的数据来源,用来确定是否需要 core-js 填充某些内容。 caniuseopen in new windowmdnopen in new windowcompat-tableopen in new window 是很好的教育资源,但是并不意味着他们能够作为数据源被开发者使用:只有 compat-table 包函好的 ES 相关数据集,它被 @babel/preset-env 使用,但是仍有些限制:

  • 它包含的数据仅仅关于 ECMAScript 特性和提案,和 web 平台特性例如 setImmediate 或者 DOM 集合迭代器没有关系。所以直到现在,@babel/preset-env 仍然通过 core-js 添加全部的 web 平台特性即使他们已经支持了。

  • 它不包含任何浏览器(甚至是严重的)bug 信息:例如,上文提到的在 Safari 12 中 Array#reverse,但是 compat-table 并没有将它标记为不支持。另一方面,core-js 已经修复了这个错误实现,但是因为 compat-table 关系,并不能使用它。

  • 它仅包函一些基础的、简单的测试,没有检查功能在真实环境下是否可以正常工作。例如,老版本 Safari 的破坏的迭代器没有 .next 方法,但是 compat-table 表明 Safari 支持,因为它用 typeof 方法检测迭代器方法返回了 "function"。一些像 typed arrays 的功能几乎没有覆盖。

  • compat-table 不是为了向工具提供数据而设计的。我是 compat-table 的维护者之一,但是其他的维护者反对为维护这个功能open in new window

因为这个原因,我创建了 core-js-compatopen in new window:它提供了对于不同浏览器 core-js 模块的必要性数据。当使用 core-js@3 时,@babel/preset-env 将使用新的包取代 compat-table请帮助我们测试并提供缺少的引擎的数据的映射关系!open in new window😊。

在 Babel 7.3 之前,@babel/preset-env 有一些与 polyfills 注入顺序有关的问题。从 7.4.0 开始,@babel/preset-env 只按推荐顺序增加需要的 polyfills 。

useBuiltIns: entry with corejs: 3

当使用这个选项时,@babel/preset-env 代替直接引用 core-js 而是引入目标环境特定需要的模块。

在这个变化前,@babel/preset 仅替换 import '@babel/polyfill'import 'core-js',他们是同义词用来 polyfill 所有稳定的 JavaScript 特性。

现在 @babel/polyfill 弃用了,当 corejs 设置为 3 时 @babel/preset-env 不会转译他。

core-js@3 中等价替换 @babel/polyfill

import "core-js/stable";
import "regenerator-runtime/runtime";

当目标浏览器是 chrome 72 时,上面的内容将被 @babel/preset-env 转换为

import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopaables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/web.immediate";

当目标浏览器是 chrome 73(它完全支持 ES2019 标准库),他将变为很少的引入:

import "core-js/modules/web.immediate";

自从 @babel/polyfill 被弃用,转而使用分开的 core-jsregenerator-runtime,我们能够优化 regenerator-runtime 的导入。因为这个原因,如果目标浏览器原生支持 generators ,那么 regenerator-runtime 的导入将从源代码中移除。

现在,设置 useBuiltIns: entry 模式的 @babel/preset-env 编译所有能够获得的 core-js 入口和他们的组合。这意味着你能够自定义,通过使用不同的 core-js 入口,它将根据的目标环境优化。

例如,目标环境是 chrome 72

import "core-js/es";
import "core-js/proposals/set-methods";
import "core-js/features/set/map";

将被替换为

import "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/esnext.set.difference";
import "core-js/modules/esnext.set.intersection";
import "core-js/modules/esnext.set.is-disjoint-from";
import "core-js/modules/esnext.set.is-subset-of";
import "core-js/modules/esnext.set.is-superset-of";
import "core-js/modules/esnext.set.map";
import "core-js/modules/esnext.set.symmetric-difference";
import "core-js/modules/esnext.set.union";

useBuiltIns: usage with corejs: 3

当使用这个选项时,@babel/preset-env 在每个文件的开头引入目标环境不支持、仅在当前文件中使用的 polyfills。

例如,

const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

当目标环境是老的浏览器例如 ie 11,将转换为

import "core-js/modules/es.array.includes";
import "core-js/modules/es.array.iterator";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.set";

const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

当目标是 chrome 72 时不需要导入,因为这个环境需要 polyfills:

const set = new Set([1, 2, 3]);
[1, 2, 3].includes(2);

Babel 7.3 之前,useBuiltIns: usage 不稳定且不是足够可靠:许多 polyfills 不包函,并且添加了许多不是必须依赖的 polyfills。在 Babel 7.4 中,我尝试使它理解每种可能的使用模式。

在属性访问器、对象解构、in 操作符、全局对象属性访问方面,我改进了确定使用哪个 polyfills 的技术。

@babel/preset-env 现在注入语法特性所需的 polyfills:使用 for-of 时的迭代器,解构、扩展运算符和 yield 委托;使用动态 import 时的 promises,异步函数和 generators,等。

Babel 7.4 支持注入提案 polyfills。默认,@babel/preset-env 不会注入他们,但是你能够通过 proposals 标志设置:corejs: { version: 3, proposals: true }

@babel/runtime

当使用 core-js@3 时, @babel/transform-runtimeopen in new window 现在通过 core-js-purecore-js的一个版本,不会污染全局变量) 注入 polyfills。

通过将 @babel/transform-runtime 设置 corejs: 3 选项和创建 @babel/runtime-corejs3 包,已经将 core-js@3@babel/runtime 集成在一起。但是这将带来什么好处呢?

@babel/runtime 的一个受欢迎的 issue 是:不支持实例方法。从 @babel/runtime-corejs3 开始,这个问题已经解决。例如,

array.includes(something);

将被编译为

import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";

_includesInstanceProperty(array).call(array, something);

另一个值得关注的变化是支持 ECMAScript 提案。默认情况下的,@babel/plugin-transform-runtime 不会为提案注入 polyfills 并使用不包含提案的入口。但是正如你在 @babel/preset-env 中做的那样,你可以设置 proposals 标志去开启:corejs: { version: 3, proposals: true }

没有 proposals 标志,

new Set([1, 2, 3, 2, 1]);
string.matchAll(/something/g);

将被编译为:

import _Set from "@babel/runtime-corejs/core-js-stable/set";

new _set([1, 2, 3, 2, 1]);
string.matchAll(/something/g);

当设置 proposals 后,将变为:

import _Set from "@babel/runtime-corejs3/core-js/set";
import _matchAllInstanceProperty from "@babel/runtime-corejs/core-js/instance/match-all";

new _Set([1, 2, 3, 2, 1]);
_matchAllInstanceProperty(string).call(string, /something/g);

有些老的问题已经被修复了。例如,下面这种流行的模式在 @babel/runtime-corejs2 不工作,但是在 @babel/runtime-corejs3 被支持。

myArrayLikeObject[Symbol.iterator] = Array.prototype[Symbol.iterator];

尽管 @babel/runtime 早期版本不支持实例方法,但是使用一些自定义的帮助函数能够支持迭代([Symbol.iterator]() 和他的 presence)。之前不支持提取 [Symbol.iterator] 方法,但是现在支持了。

作为意外收获,@babel/runtime 现在支持 IE8-,但是有些限制,例如,IE8- 不支持访问器、模块转换应该用松散的方式,regenerator-runtime(内部使用 ES5+ 实现)需要通过这个插件转译。

展望未来

即使做了许多工作,但是 core-js 距离完美还很远。这个库和工具将来应该如何改进?语言的变化将会如何影响它?

支持老的引擎

现在 core-js 尽可能支持所有引擎和能够测试到的平台:甚至是 IE8-,或者如早期版本的 Firefox 等。虽然它对某些用户有用,但是仅有一小部分使用 core-js 的开发者需要它。对于大多数用户,这会导致包体积过大或运行缓慢。

主要的问题源自于支持 ES3 引擎(首先是 IE8- ):多数现代 ES 特性基于 ES5,这些功能在老版本浏览器中均不可用。

最大的缺失特性是属性描述符:当它缺失时,一些功能无法 polyfill,因为他们要么是访问器(像 RegExp.prototype.flagsURL 属性的 setter )要么就是基于访问器(像 typed array polyfill)。为了解决这个问题,我们需要使用不同的解决方法(例如,保持更新 Set.prototype.size)。维护这些解决方法有时很痛苦,移除它们将极大的简化许多 polyfill。

然而,描述符仅仅是问题的一部分。ES5 标准库包含了很多其他特性,他们被认为是现代 JavaScript 的基础:Object.createObject.getPrototypeOfArray.prototype.forEachFunction.prototype.bind 等等。和多数现代特性不同,core-js 内部依赖他们,并且为了实现一个简单的现代函数,core-js 需要加载其中一些"建筑模块"的实现open in new window。对于想要创建一个足够小的构建包和仅仅想要引入部分 core-js 的用户来说,这是个问题。

在一些国家 IE8 仍很流行,但是为了让 web 向前发展,浏览器到了某些时候就应该消失了。 IE8 在 2009 年 3 月 19 日发布,到今天已经 10 年了。IE6 已经 18 岁了:几个月前新版的 core-js 已经不再测试 IE6 了。

core-js@4 我们应该舍弃 IE8- 和其他不支持 ES5 的引擎。

ECMAScript 模块

core-js 使用 CommonJS 模块规范。长期以来,他是最受欢迎的 JavaScript 模块规范,但是现在 ECMAScript 提供了他自己的模块规范。许多引擎已经支持它了。一些构建工具(像 rollup )基于它,其他的构建工具提供它作为 CommonJS 的替代。这意味提供了一个可选择的使用 ESMAScript 模块规范版本的 core-js 行得通。

支持 web 标准扩展?

core-js 当前专注在 ECMAScript 支持,但是也支持少量的跨平台以及和 ECMAScript 紧密联系的 web 标准功能。为 web 标准添加像 fetch 的这种的 polyfill 是受欢迎的功能请求。

core-js 没有增加他们的主要原因是,他们将严重的增加构建包大小并且将强制 core-js 用户载入他们可能用不到的功能。现在 core-js 是最大限度的模块化,用户能够仅选择他们需要的功能,这就像 @babel/preset-env@babel/runtime 能够帮助用户去减少没用到和不必要的 polyfills。

现在是时候重新审视这个决定了?

针对目标环境的 @babel/runtime

目前,我们不能像对 @babel/preset-env 那样为 @babel/runtime 设置目标加环境。这意味即使目标是现代浏览器, @babel/runtime 也将注所有可能的 polyfills:这不必要的增加了最终构建包的大小。

现在 core-js-compat 包函全部必要数据,将来,可以在 @babel/runtime 中添加对目标环境的编译支持,并且在 @babel/preset-env 中添加 useBuiltIns: runtime 选项。

更好的优化 polyfill 加载

正如上面解释的,Babel 插件给了我们不同的方式去优化 core-js 的使用,但是他并不完美:我们可以改进他们。

通过 useBuiltIns: usage 选项,@babe/preset-env 能够做的比之前更好,但是针对一些不寻常的例子他们仍然会失败:当代码不能被静态分析。针对这个问题,我们需要为库开发者寻找一个方式去确定哪种 polyfill 是他们的库需要的,而不是直接载入他们:某种元数据 -- 将在创建最终构建包时注入 polyfill。

另一个针对 useBuiltIns: usage 的问题是重复的 polyfill 导入。useBuiltIns: usage 能够在每个文件中注入许多 core-js 的导入。但如果我们的项目有数千个文件或者即使十分之一会怎么样呢?这种情况下,与导入 core-js 自身相比,导入 core-js/... 将有更多代码行:我们需要一种方式去收集所有的导入到一个文件中,这样才能够删除重复的。

几乎每一个需要支持像 IE11 浏览器的 @babel/preset-env 用户都为每个浏览器使用同一个构建包。这意味着完全支持 ES2019 的现代浏览器将加载不必要的、仅仅是 IE11 需要的 polyfills。当然,我们可以为不同的浏览器创建不同的构建包来使用,例如,type=module / nomodules 属性:一个构建包给支持模块化的现代浏览器,另一个给传统浏览器。不幸的是,这不是针对这个问题的完整的解决方案:基于 UA 打包目标浏览器需要的 polyfill 的服务非常有用。比如现有的polyfill-serviceopen in new window。尽管很有趣也很流行,但是它的 polyfill 的质量还有很多不足。它不像几年前那么差:项目团队积极工作去改变它,但是如果你想用他们匹配原生实现,我不建议你通过这个项目使用 polyfill。许多年前我尝试通过这个项目将 core-js 作为 polyfill 的源,但是这不可能。因为 polyfill-service 依赖文件嵌套而不是模块化(就像 core-js 发布后的前几个月 😊)。

像这样一个集成了一个很棒的 polyfill 源 -- core-js 的服务,通过像 Babel 的 useBuiltIns: usage 选项,静态分析源代码真的能够引起我们对于 polyfill 思考方式的革命。

来自 TC39 的新功能预案和 core-js 可能的问题

TC39 一直在努力工作去改进 ECMAScript:你可以通过查看 core-js 中实现所有新提案查看进度。然而,我认为有些新的提案功能在 polyfill 或者转译时可能引起严重的问题。关于这个足够可以写一篇新的文章,但是我将尝试在这总结一下我的想法。

标准库提案,stage 1

现在,TC39 考虑给 ECMAScript 增加内置模块open in new window:一个模块化的标准库。它将成为 JavaScript 的最佳补充,而 core-js 是它可以被 polyfill 的最佳位置。根据 @babel/preset-env@babel/runtime 用到的技术,理论上我们可以通过一种简单的方式注入内置模块需要的 polyfill。然而,这个提案的当前版本会导致一些严重问题,这些问题并没有使其简单明了。

内置模块的 polyfill,根据作者的提案open in new window,仅仅意味着退回到分层 API 或者 导入 maps。这表明如果原生模块缺失,它将能够通过提供的 url 载入一个 polyfill。这绝对不是 polyfill 需要的,并且它与 core-js 的架构以及其他流行的 polyfill 都不兼容。导入 maps 不应该是 polyfill 内置模块的唯一方式。

我们通过一个特定前缀使用 ES 模块语法就能够得到内置模块。这个语法在语言的早期版本并没有对等的 - 转译模块不可能在现在浏览器中与未转译的交互 - 这会导致包分发的问题。

更进一步讲,他将异步工作。对于功能检测这是个严重的问题 - 当你要检测一个功能并且加载 polyfill 时脚本不会等待 - 功能检测应该同步的做。

在没有转译和 polyfill 的情况下第一次实现内置模块open in new window。如果没有修改,在当前的 core-js 格式下内置模块将不可能 polyfill。建议的 polyfill 方式将使开发变得严重复杂。

这个标准库的问题能够通过添加一个新的全局变量解决(这将是最后一个吗?):一个内置模块的注册表将允许异步的设置和获取,例如:

StandardLibraryRegistry.get(moduleName);
StandardLibraryRegistry.set(moduleName, value);

异步回调,比如分层 API 应该全局注册表之后使用。

值得一提的是,它将简化将本地模块导入到老的语法的转换。

装饰器提案,新的迭代器语法,stage 2

这个提案中的 新迭代器open in new window,他被很认真的重做了。装饰器定义不再是语法糖,就像内置模块,我们不能在老版本的语言中编写装饰器并将其用作原生装饰器。除此之外,装饰器不仅仅是普通的标识符 - 他们生活在平行的词汇范围内:这意味着已经编译的装饰器不能和原生装饰器交互。

提案作者建议使用未编译的装饰器发布包,让包的使用者选择去编译他们的依赖。然而,在不同的情况下是不可能的。当他们被添加到 JS 标准库时,这个方法将阻止 core-js polyfill 新的内置装饰器。

装饰器应该是在某些东西上应用功能的一种方法,他们应该仅仅是包裹的语法糖。为什么要复杂化呢?


如果引入的一个语言功能不是从根本上是新的,在语言的早期版本什么不应该实现是可以选择的,我们能够转译或者 polyfill 它,被转译或者 polyfill 的代码应该能够和支持这个功能的浏览器原生交互。

我希望根据提案作者和委员会的智慧,这些提案能够被采纳,这样才能够合理的转译或者 polyfill 他们。


如果你对 core-js 项目感兴趣,或者你在你日常工作中使用它,你可以成为 OpenCollectiveopen in new window 或者 Patreonopen in new window 捐赠者。core-js 的背后不是一个公司:他的将来要靠你。


这里open in new window 可以评论这篇文章。

Denis Pushkarevopen in new window,2019 年 3 月 19 日,感谢 Nicolò Ribaudoopen in new window 编辑。