上一讲中, 我们介绍了前端模块化的发展历史 一起梳理了一下 CommonJS,AMD,CMD以及UMDESM等模块化规范的发展以及含义. 本章开始, 我们则从 JS语言出发, 了解并学习其背后的ECMAScript规范以及 编译器 Babel

1. ECMAScript 与 JavaScript 的关系

ECMAScriptJavaScript的语言标准, 是有版本迭代的, 比如我们耳熟能详的ES6就是ECMAScript的第6个版本(也就 ES2015即2015年的版本), 当然ECMAScript标准还有其他的实现, 如NodeJS``JScript``ActionScript等.
引自 https://es6.ruanyifeng.com/#docs/intro

1996年11月, JavaScript的创造者公司Netscape, 决定将JavaScript提交给标准化组织 ECMA, 希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。 该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

ECMAScript 1.0 是 1997 年发布的,接下来的两年,连续发布了 ECMAScript 2.0(1998 年 6 月)和 ECMAScript 3.0(1999 年 12 月)。3.0 版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习 JavaScript,其实就是在学 3.0 版的语法。

2000 年,ECMAScript 4.0 开始酝酿。这个版本最后没有通过,但是它的大部分内容被 ES6 继承了。因此,ES6 制定的起点其实是 2000 年。

为什么 ES4 没有通过呢?因为这个版本太激进了,对 ES3 做了彻底升级,导致标准委员会的一些成员不愿意接受。ECMA 的第 39 号技术专家委员会(Technical Committee 39,简称 TC39)负责制订 ECMAScript 标准,成员包括 Microsoft、Mozilla、Google 等大公司。

2007 年 10 月,ECMAScript 4.0 版草案发布,本来预计次年 8 月发布正式版本。但是,各方对于是否通过这个标准,发生了严重分歧。以 Yahoo、Microsoft、Google 为首的大公司,反对 JavaScript 的大幅升级,主张小幅改动;以 JavaScript 创造者 Brendan Eich 为首的 Mozilla 公司,则坚持当前的草案。

2008 年 7 月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激烈,ECMA 开会决定,中止 ECMAScript 4.0 的开发,将其中涉及现有功能改善的一小部分,发布为 ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为 Harmony(和谐)。会后不久,ECMAScript 3.1 就改名为 ECMAScript 5。

2009 年 12 月,ECMAScript 5.0 版正式发布。Harmony 项目则一分为二,一些较为可行的设想定名为 JavaScript.next 继续开发,后来演变成 ECMAScript 6;一些不是很成熟的设想,则被视为 JavaScript.next.next,在更远的将来再考虑推出。TC39 委员会的总体考虑是,ES5 与 ES3 基本保持兼容,较大的语法修正和新功能加入,将由 JavaScript.next 完成。当时,JavaScript.next 指的是 ES6,第六版发布以后,就指 ES7。TC39 的判断是,ES5 会在 2013 年的年中成为 JavaScript 开发的主流标准,并在此后五年中一直保持这个位置。

2011 年 6 月,ECMAScript 5.1 版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)。

2013 年 3 月,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript 7。

2013 年 12 月,ECMAScript 6 草案发布。然后是 12 个月的讨论期,听取各方反馈。

2015 年 6 月,ECMAScript 6 正式通过,成为国际标准。从 2000 年算起,这时已经过去了 15 年。

但是,因为这个版本引入的语法功能太多,而且制定过程当中,还有很多组织和个人不断提交新功能。事情很快就变得清楚了,不可能在一个版本里面包括所有将要引入的功能。常规的做法是先发布 6.0 版,过一段时间再发 6.1 版,然后是 6.2 版、6.3 版等等。 但是,标准的制定者不想这样做。他们想让标准的升级成为常规流程:任何人在任何时候,都可以向标准委员会提交新语法的提案,然后标准委员会每个月开一次会,评估这些提案是否可以接受,需要哪些改进。如果经过多次会议以后,一个提案足够成熟了,就可以正式进入标准了。这就是说,标准的版本升级成为了一个不断滚动的流程,每个月都会有变动。

标准委员会最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的 6 月份,草案就自然变成了新一年的版本。这样一来,就不需要以前的版本号了,只要用年份标记就可以了。

ES6 的第一个版本,就这样在 2015 年 6 月发布了,正式名称就是《ECMAScript 2015 标准》(简称 ES2015)。2016 年 6 月,小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准。根据计划,2017 年 6 月发布 ES2017 标准。

因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。

2. ECMAScript版本特性

ES3

因为 ECMAScript要晚于 JavaScript, 因此在前面几个版本, 主要是对 JavaScript进行标准化, 我找了下面的一个表格, 可以看到在 ES3版本时标准的语法内容, 基本上当时的 JavaScript已经全部支持了.

特性 JavaScript 1.2 JScript 3.0 ECMA-262 第 3 版
do 语句
break/continue 到标签
switch 语句
嵌套函数
函数表达式
对象字面量
数组字面量
=== 和 !==
正则表达式字面量
delete 运算符
所有对象上的 proto 伪属性
数组方法 concat, slice
数组方法 push, pop, shift, splice, unshift
带有继承元素的稀疏数组
使用正则表达式的字符串方法 fromCharCode, match, replace, search, substr, split
字符串方法 charCodeAt
正则表达式方法 compile, exec, test
正则表达式属性 $1…$9, input
正则表达式全局属性 lastMatch, lastParen, leftContext, rightContext
带有本地声明属性的 arguments 对象
arguments.callee
arguments.caller
watch/unwatch 函数
import/export 语句与脚本签名
条件编译
debugger 关键字

ES5

ES5进一步完善了 ECMAScript, 主要特性主要有,

  1. 提供了严格模式,
  2. 增强内置对象的方法
  3. 属性 Getter 和 Setter

如数组的方法

  • Array.isArray()
  • Array.forEach()
  • Array.map()
  • Array.filter()
  • Array.reduce()
  • Array.reduceRight()
  • Array.every()
  • Array.some()
  • Array.indexOf()
  • Array.lastIndexOf()

字符串的方法

  • String.trim()

日期对象的方法

  • Date.now()

还新增了JSON的两个常用方法

  • JSON.parse()
  • JSON.stringify()

同时也扩展了Object的很多方法, 如

  1. // 添加或更改对象属性
  2. Object.defineProperty(object, property, descriptor)
  3. // 添加或更改多个对象属性
  4. Object.defineProperties(object, descriptors)
  5. // 访问属性
  6. Object.getOwnPropertyDescriptor(object, property)
  7. // 将所有属性作为数组返回
  8. Object.getOwnPropertyNames(object)
  9. // 将可枚举属性作为数组返回
  10. Object.keys(object)
  11. // 访问原型
  12. Object.getPrototypeOf(object)
  13. // 防止向对象添加属性
  14. Object.preventExtensions(object)
  15. // 如果可以将属性添加到对象,则返回 true
  16. Object.isExtensible(object)
  17. // 防止更改对象属性(而不是值)
  18. Object.seal(object)
  19. // 如果对象被密封,则返回 true
  20. Object.isSealed(object)
  21. // 防止对对象进行任何更改
  22. Object.freeze(object)
  23. // 如果对象被冻结,则返回 true
  24. Object.isFrozen(object)

ES6(ES2015)

ES6大家应该是非常熟悉的, 因为经过十几年的制定, 积累了大量的内容, 可以说是划时代的版本.
ES6的新特性非常的多, 我们这里简单的罗列一下, 具体的可以去看阮一峰老师写的 ECMAScript6入门(因为ES6是一个大版本, 所以后续的 ES2016,ES2017这类小版本也是归纳在ES6中的, 我们这里仅列举ES2015的新特性),在参考链接里面有.

1.letconst

2.解构赋值

3.字符串扩展(迭代遍历, 模板字符串

4.字符串新增方法

fromCodePoint(),raw(),codePointAt(),normalize(),includes(),startsWith(),endsWith(),repeat()

5.正则表达式(各种修饰符)

6.数值扩展(isFinite(),isNaN(),isInteger(), Math对象的方法扩展)

7.函数扩展(参数默认值, rest(...)参数, 箭头函数)

8.数组扩展

扩展运算符..., Array.from(), Array.of``Array.copyWithin(),find(),findIndex(),findLast(),findLastIndex(), fill(),entries(), keys(), values(), includes(),flat(), flatMap())

9对象扩展

简洁属性引用, 属性表达式, ,super关键字

10 对象新增方法

Object.is()(和===有区别)Object.assign()

11.Symbol

12.Set(WeakSet)Map(WeakMap)

13.Proxy

14.Reflect

15.Promise

16.Iteratorfor...of

17.Generator

18.Class

19.Module(模块化)

ES2016

1.Array.prototype.includes()

2.幂运算 a ** b = Math.pow(a, b);

ES2017

1.String.prototype.padStart, String.prototype.padEnd
  1. ("1").padStart(2, "0"); // "01"

2.async/await

Promise处理异步的语法糖, 可以像写同步代码一样写异步, 规避回调地狱.

3.Object.entries()

返回对象自身可枚举属性的键值对数组
4. Object.values()
返回对象自身的所有属性值, 不包括继承的值
5. Object.getOwnPropertyDescriptors
返回对象所有自身属性的描述符.
6函数参数列表结尾允许逗号
7.SharedArrayBuffer
共享的内存空间, 区别于ArrayBuffer是不能被转移(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
8. Atomics
提供静态方法对ShaderArrayBuffer进行原子操作.

ES2018

1.对象的 rest/spread运算符

在ES2015中, 为数组引入了 rest/spread运算符, 我们可以用以简单的替换到 concatslice方法.
在ES2018中, 我们同样的操作可以用在对象上了, 如

  1. const a = {b: 1, c: 2};
  2. const d = {...a}; // Object.assign({}, a);
  3. const {b, ...e} = a; // e: {c: 2}

2.异步迭代Symbol.asyncIterator

异步迭代器与传统迭代器的不同点在于,它不返回 {value,done} 的形式的普通对象,而是返回一个完成(fulfill) {value,done} 的promise. 同样的, 异步迭代的遍历, 不能直接用for..of, 而是 for…await…of

  1. for await (const x of collection) {
  2. console.log(x);
  3. }

3. Promise.prototype.finally

4.RegExp新特性, /s(dotAll), 可命名捕获组, Lookbehind断言与Unicode属性转义

其中比较有意思的是可命名捕获组, 正常我们在用正则匹配需要的字符串时, 一般需要用exec返回的match数组, 比如:

  1. const
  2. reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
  3. match = reDate.exec('2018-04-30'),
  4. year = match[1], // 2018
  5. month = match[2], // 04
  6. day = match[3]; // 30

而可命名捕获组则可以用 ?<name>来给捕获的结果指定name, 然后我们直接通过name就能拿到我们要的值了.

  1. const
  2. reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  3. match = reDate.exec('2018-04-30'),
  4. year = match.groups.year, // 2018
  5. month = match.groups.month, // 04
  6. day = match.groups.day; // 30

同样的, 命名捕获也可以用在String.prototype.replace()中.

  1. const
  2. reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  3. d = '2018-04-30',
  4. usDate = d.replace(reDate, '$<month>-$<day>-$<year>');

ES2019

1. try..catch可选参数e

之前我们写 try...catch都要加上异常变量 e, 如:

  1. try {
  2. ...
  3. } catch(e) {
  4. ...
  5. }

现在, 这个e是可选的了, 比如

  1. try {
  2. ...
  3. } catch {
  4. ...
  5. }

2. Symbol.prototype.description

用以修饰 Symbol

  1. const s = Symbol('foo');
  2. console.log(s.description); // foo
  3. const s1 = Symbol();
  4. console.log(s1.description); // undefined

3.Function.prototype.toString

计入标准, 要求返回函数的源代码.

4.Object.fromEntries

Object.entries刚好相反. 如:

  1. const obj = {a: 1, b: 2};
  2. const entries = Object.entries(obj); // [["a", 1], ["b", 2]];
  3. const obj2 = Object.fromEntries(entries); // obj.

5. Array.prototype.flat 与 Array.prototype.flatMap

flat方法用于把数组拍平, 相当于把_.flatten, _.flattenDeep, _.flattenDepth三个方法合成一个方法, 如:

  1. // 不传参数的话, 默认拍平一层
  2. [1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]]]
  3. // 也可以指定拍平的层数
  4. [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
  5. // 直接拍到最底层可以传 Infinity
  6. [1, 2, [3, [4, 5, [6, 7]]]].flat(Infinity) // [1, 2, 3, 4, 5, 6, 7]

flatMap则是 flat + map, 如

  1. let arr = [1, 2, 3, 4];
  2. arr.flatMap(x => [x*2]) // [2, 4, 6, 8]

好处是, 一个方法, 把两个操作都做了, 效率比较高.

6.String.prototype.timStart 与 String.prototype.trimEnd

trim用于删除字符串两端的空字符串, 同样的, trimStart则是只删除前面, trimEnd只删除后面.

ES2020

1. String.prototype.matchAll

返回一个捕获分组的迭代器, 与 match相比的话, 分组结果更明确了.

  1. var regexp = /t(e)(st(\d?))/g;
  2. var str = 'test1test2';
  3. str.match(regexp);
  4. // Array ['test1', 'test2']
  5. let array = [...str.matchAll(regexp)];
  6. array[0];
  7. // ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
  8. array[1];
  9. // ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]

2.动态import

  1. import('./a.js').then(module => {
  2. console.log(module.hello()); // Hello World !
  3. })

3. BigInt

之前在JS中, 最大的安全整数时 2^53 - 1
image.png
而使用BigInt.
image.png

4. Promise.allSettled.

Promise.all相近, 区别于 Promise.all会短路, 即只要有一个Promise对象进入rejected状态, Promise.all就会提前结束.
但是Promise.allSettled则不会, 会等所有的Promise都结束了再结束

5.globalThis

浏览器环境下全局对象是window, node环境下全局对象则是 global, 在webworker中, 全局对象又是self….
所以, 我们写的代码, 如果想要支持跑在更多的环境中, 需要手动处理一下全局对象的获取, 比如 underscore里面获取全局对象的判断

  1. var root = (typeof self == 'object' && self.self == self && self) ||
  2. (typeof global == 'object' && global.global == global && global) ||
  3. this;

现在我们可以直接使用 globalThis来获取全局变量

6.链判断运算符 ?.

  1. var c = a?.b; // a == null ? undefined : a.b;
  2. d.e?.(); // d.e && d.e()

null判断运算符 ??

7. null判断运算符 ??

  1. null ?? 1; // 1
  2. undefined ?? 1; // 1;
  3. // 类似于 a == null ? 1 : a;

8. import.meta

ESModule里面, 可以使用 import.meta查看当前模块的信息, 如

  1. import.meta.url; // xxx.js

.
vite中, 则是直接把全局参数注入到 import.meta中使用

ES2021

1.String.prototype.replaceAll

之前我们想对字符串做批量替换, 得用正则表达式 /xxx/g
而写过java的同学都知道, 其中有一个string.replaceAll可以批量替换, 现在在 js里面也可以这么写了

2. Promise.any

传入一组可迭代的Promise集合, 返回一个新的 Promise, 并且当集合中出现一个进入fullfilled的Promise出现时, 返回的 Promise也会被标记为fullfilled, 如果集合中没有出现fullfilledPromise, 那么则返回rejected.
类似于 Array.prototype.any, 即出现第一个满足条件的就终止, 否则则失败.
我们需要关注一下其与 Promise.allPromise.race的区别
Promise.all返回的是一组Promise的状态, Promise.anyPromise.race只返回一个.
而与Promise.race的区别在于, 后者返回第一个完成或者失败的Promise(最快结束). 而Promise.any则会尝试寻找第一个完成的Promise, 都没有才返回rejected.

3. WeakRefs

弱引用, 不会影响垃圾回收, 和 WeakMap差不多, 区别在于可以直接引用一个对象, 不需要什么key了. 使用方法

  1. var foo = () => {console.log('hi')};
  2. var weakFoo = new WeakRef(foo);
  3. // 使用 deref()获取原始的对象.
  4. console.log(weakFoo.deref()) // () => {console.log('hi')}

4.逻辑运算符 ||=, &&=, ??=

  1. // 或赋值运算符
  2. x ||= y
  3. // 等同于
  4. x || (x = y)
  5. // 与赋值运算符
  6. x &&= y
  7. // 等同于
  8. x && (x = y)
  9. // Null 赋值运算符
  10. x ??= y
  11. // 等同于
  12. x ?? (x = y)

5.数值分割符

  1. 1000000 == 1_000_000; // 更好阅读了

ES2022

1. 类的字段定义, 私有属性

  1. class a {
  2. b = 1;
  3. #c = 2;
  4. #d() {
  5. console.log(this.#c);
  6. }
  7. }

2. RegExp Match Indices

给正则表达式加一个/d的修饰符, 这样使用exec()方法后, 返回的结果会多一个 indices属性, 表示匹配字符串在原始字符串中的索引.
image.png

3. Top-level await

以前我们写 async...await, await必须要在async内部.
最新的提案允许我们直接使用 await

4. in 方法可以判断私有属性

  1. class C {
  2. #brand;
  3. #method() {}
  4. get #getter() {}
  5. static isC(obj) {
  6. return #brand in obj && #method in obj && #getter in obj;
  7. }
  8. }

5. at()

新增的取值方法, 可以用于 Array, String, TypedArray
和直接用[]区别在于, 可以用负数取倒数第n个元素

6. Object.hasOwn();

改良版本的Object.prototype.hasOwnProperty()

  1. // object继承自对象{ foo: 'foo' }
  2. const object = Object.create({ foo: 'foo' })
  3. // 再给本身添加属性bar
  4. object.bar = 'bar'
  5. object.hasOwnProperty('foo') // false
  6. object.hasOwnProperty('bar') // true
  7. Object.hasOwn(object, 'foo') // false
  8. Object.hasOwn(object, 'bar') // true

值得注意的是, 如果直接Object.create(null)得到的对象是没有继承Object.prototype, 因此不能直接用hasOwnProperty, 使用Object.hasOwn可以规避这个问题.

7. 静态作用域

给一个静态代码块, 可以直接对执行对静态变量做修改, Java里面已经有了.

  1. class A {
  2. static b = 1;
  3. static {
  4. if (c()) {
  5. this.b = 2;
  6. }
  7. }
  8. }

Babel

上面我们了解到了 ECMAScript以及每个版本的新特性, 而我们要想在实际编码中, 使用这些新的特性, 就不得不考虑浏览器兼容性的问题, 毕竟我们用了2020的新特性, 但是有可能跑在2019年发布版本的浏览器上.

为此, 我们需要把高版本的语法, 转换成低版本的, 从而保证可以在不同版本的浏览器上正常运行, 这个转换就需要用到Babel.

一般来说, 我们只需要兼容到 ES5版本(2009年发布)即可, 更低的版本理论上也能兼容, 但是那种古董级的浏览器, 使用占比已经很小很小了, 我刚来公司的时候, 图表还要兼容IE6, 现在基本只要求到IE11就可以了.

虽然现在很多集成的cli已经帮我们处理好了 babel的配置, 如 react-scripts, vite等, 但是作为行业内最优秀的语法转换库, 我们还是有必要了解一下其使用方式以及背后的工作原理.

Babel的工作内容主要分成三个部分, 解析, 转换, 生成.
其中, Babel自身是不处理任何转换操作的, 只负责解析JS的源码, 生成ast的语法树, 然后把转换的工作, 交给不同的插件去做, 每个插件只做一个语法特性的转换, 所以我们要把 诸如es6的全部特性都转成es5的话,就得安装一堆转换的插件, 如duchamp@babel下的转换插件就有好几十个.
image.png
所以我们在使用Babel的时候, 主要工作就是根据需要安装配置插件.
当然, 一次性安装这么多转换插件, 第一是记不住, 第二一个一个安装还是很麻烦的。
所以 Babel官方也给我们提供了常用的语言标准转换所需要的插件合集, 即

babel-preset,

里面包含了我们常用的几种语言转换规则,如 env,react,typescript(没错,虽然tsc已经支持直接把ts转成指定版本的js,但是babel提供的转换规则给细致,而且自定义程度更高,所以一些开发者习惯于用babel编译ts,而typescript本身只做类型检查)

上面我们说的几个都是 最新的 babel 7.x版本内置的, 早些版本的 babel-preset还有stage-xes201x
前者用以表示当前还在 ECMAScript提案中或者已经纳入标准但是还没发布的新语法,其中还细分有:

  • Stage 0 - 稻草人: 只是一个想法,经过 TC39 成员提出即可。
  • Stage 1 - 提案: 初步尝试。
  • Stage 2 - 初稿: 完成初步规范。
  • Stage 3 - 候选: 完成规范和浏览器初步实现。
  • Stage 4 - 完成: 将被添加到下一年度发布。

后者则是直接指定具体的年份版本

不过这两个在最新的 Babel7都被删掉了, 因为env已经足够强大,而且配置更灵活
现在我们只需要一个@babel/preset-env即可覆盖到最新的ECMAScript标准
关于 @babel/preset-env内置的插件以及作用, 可以参考:

当然除了 babel-preset之外, babel还提供了其他的一些转换工具如

babel-cli

最新版的包叫做 @babel/cli,就是命令行工具,安装了这个就可以直接在命令行里面使用 babel对指定的文件进行转换。

babel-node

之前是集成在 babel-cli中, babel7独立出来, 叫做@babel/node
用以在低版本node环境中,使用高版本的 ECMAScript,我们感觉用不到, 因为我们都是用的比较新的node天然支持新特性语法

babel-register

可以改写 nodejs中的require执行, 并挂载hooks,从而对使用require加载的文件, 使用babel转码

babel-polyfill

babel默认只会对新语法进行转换,比如 let,const箭头函数class
而对于一些新的APIProxy,Symbol,Set等,以及各种对象新增的方法Object.assign则是不转换的.
为此, 我们需要使用 babel-polyfill对这些新的API进行转换.
babel-polyfill也有缺点:

  1. 使用 babel-polyfill 会导致打出来的包非常大,因为 babel-polyfill 是一个整体,把所有方法都加到原型链上。比如我们只使用了 Array.from,但它把 Object.defineProperty 也给加上了,这就是一种浪费了。这个问题可以通过单独使用 core-js 的某个类库来解决,core-js 都是分开的。
  2. babel-polyfill 会污染全局变量,给很多类的原型链上都作了修改,如果我们开发的也是一个类库供其他开发者使用,这种情况就会变得非常不可控。

所以在 Babel7.4版本之后,这个包已经被标为deprecated官方推荐我们直接使用core-js/stable来代替它.

babel-run-time与babel-plugin-transform-runtime

babel-run-time是包含各种语法转换的工具库
比如

  1. class Circle {}

经过转换后变成了

  1. function _classCallCheck(instance, Constructor) {
  2. //...
  3. }
  4. var Circle = function Circle() {
  5. _classCallCheck(this, Circle);
  6. };

但是每个文件里面的class都换转换成_classCallCheck这样就形成了重复代码,为此引入了@babel/plugin-transform-runtime,它会把这些方法替换成babel-rumtime中的工具方法引用, 如:

  1. var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
  2. var Circle = function Circle() {
  3. _classCallCheck(this, Circle);
  4. };

参考: