深入理解 Babel - 微内核架构与 ECMAScript 标准化|得物技术

news2024/9/22 19:38:49

随着浏览器版本的持续更新,浏览器对JavaScript的支持越来越强大,Babel的重要性显得较低了。但Babel的设计思路、背后依赖的ECMAScript标准化思想仍然值得借鉴。

本文涉及的Babel版本主要是V7.16及以下,截至发文时,Babel最新发布的版本是V7.25.6,未出现大版本更新,近2年也进入了稳定迭代期,本文的分析思路基本适用目前的Babel设计。

一、Babel简介

Babel是什么

Babel是JavaScript转译器,通过Babel,开发者可以自由使用下一代ECMAScript 语法。高版本ECMAScript语法将被转译为低版本语法,以便顺利运行在各类环境,如低版本浏览器、低版本 Node.js 等。

Babel 是转译器,不是编译器。下面是转译和编译的区别:

编译,一般指将一种语言转换为另一种语法和抽象程度等都不同的语言,常见的比如 gcc 编译器。

转译,一般指将一种语言转换为不同版本或者抽象程度相同的语言,比如 Babel 可以把 ECMAScript 6 语法转译为 ECMAScript 5语法。

利用 Babel,开发者可以使用 ECMAScript 的各种新特性进行开发,同时花极少的精力关注浏览器或其他JS运行环境对新特性的支持。甚至,开发者可以根据自身需要,创造属于自己的 JavaScript 语法。

Babel在转译的时候,会对源码进行以下处理: 语法转译(Syntax)和添加API Polyfill。

01.jpg

  • 语法(Syntax)部分
    Babel 支持识别高版本语法,并通过插件将源码从高版本语法转译为低版本语法,如:

    • 箭头函数 () => {} 转为普通函数 function() {}。

    • const / let 转译为var

  • API Polyfill

有些运行时相关的 API,语法转译无法解决它们对低版本浏览器等环境的兼容性问题,因此 Babel 通过与 core-js 等工具的配合,实现 API 部分对目标环境(通常是低版本浏览器等)的兼容。

例如[1, 2, 3].include、Promise等 API,Babel 在处理时,如果目标环节可能不支持原生的 include / Promise 的话,Babel 会在转译结果中嵌入 include / Promise 的自定义实现。

有多种方式可以使用 Babel,如: 命令行(babel-cli、babel-node)、浏览器(babel-standalone)、API 调用(babel-core)、webpack loader(babel-loader)等。

转译过程

和多数转译器相同,Babel 运行的生命周期主要是 3 个阶段: 解析、转换、代码生成。

这个过程涉及抽象语法树:

抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。

AST 是树形对象,以结构化的形式表示编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

02.jpg

源码字符串需要经转译器生成 AST,转译器有很多种,不同转译器,生成的AST对象格式细节可能有差异,但共同点为: 都是树形对象、该树形对象描述了节点特征、各节点之间的关系(兄弟、父子等)。

以下是 Babel 生命周期的三个过程:

  • 解析(Parsing): Code1 ==> 抽象语法树1
    解析过程包括 2 个环节: 词法解析、语法解析,最终生成抽象语法树。
    词法解析阶段,代码字符串被解析为 token 令牌数组,数组项是一个对象,包括: 代码字符碎片的值、位置、类型等信息。
    token 数组是平铺式的数组,没有嵌套的结构信息,它是为语法解析阶段做准备的。
    语法解析阶段,token 令牌数组被解析为结构化的抽放语法树对象(AST)。
    babel-parser 完成该阶段的主要功能。

03.jpg

  • 转换(Transformation): AST1 ==> AST2
    Babel 生成 AST 后,会对 AST 进行遍历,遍历过程中,各类插件对原 AST 对象进行增删改查等操作,AST 结构被修改。

04.jpg

  • 代码生成(Generation): AST2 ==> Code2
    Babel 将修改后的 AST 对象转目标代码字符串。
    babel-generator 完成该阶段的主要功能。

05.jpg

二、Babel微内核架构

微内核架构

Babel 采用微内核架构,其内核保留核心功能,其余功能利用外部工具和插件机制实现,也体现了"开放-封闭"的设计原则。

06.jpg

除了微内核设计架构,Babel 的模块设计也可以做如下分类:

07.jpg

转译模块

转译模块位于 Babel 微内核架构的"微内核"部分,该部分主要负责代码转译,也就是上面提到的"解析-转换-代码生成"过程。

该模块主要包括: babel-parser、babel-traverse、babel-generator。

  • babel-parser
    负责将代码字符串转为 AST 对象。
    有 2 点值得一提:

    • babel-parser 本身并不会对 AST 做转换操作,只是负责解析出 AST。AST 转换部分交由各类 plugins 和 presets 处理。

    • babel-parser 内置了对 ESNext/TypeScript/JSX/Flow 最新版本语法的支持,但很多默认是不开启的,目前没有开放插件机制扩展新语法。

  • babel-traverse
    在转译过程中,babel-traverse 负责遍历 AST 节点,并根据配置的 plugins/presets,在遍历过程中,对各个 AST 节点进行增删改查的操作。
    AST 是一个树形对象,遍历 AST 对象的过程也是一个深度优先遍历的过程。

  • babel-generator
    负责将 AST 节点,转为代码字符串,同时也可以生成 source-map。

插件模块

插件模块包括 plugins、presets。

  • plugins
    丰富的插件,帮助 Babel 成为一个非常成功的转译工具。
    对 AST 的遍历、转换是 Babel 转译的核心功能,但 Babel 本身并不参与该过程,将这些功能作为插件引入到运行时。
    具体来说,babel-core 作为核心工具,不提供对 AST 的修改逻辑,通过调用各类插件,实现对 AST 的修改。
    Babel的插件分为语法插件和转换插件。

    • 语法插件
      值得注意的是,babel-parser 负责将 JavaScript 代码解析出抽象语法树(AST),它支持全面识别 ESNext/TypeScript/JSX/Flow 等语法,目前由 Babel 团队开发维护,不支持插件化。
      Babel 插件生态中的语法插件,其功能就是作为"开关",配置是否开启 babel-parser 的某些语法转译功能。
      语法插件在 Babel 源码中,以 babel-plugin-syntax 开头。
      举个例子:

      • babel-plugin-syntax-decorators
        负责开启 babel-parser 对装饰器的语法支持。

      • babel-plugin-syntax-dynamic-import
        负责开启 babel-parser 对 import 语句的语法支持。

      • babel-plugin-syntax-jsx
        负责开启 babel-parser 对 jsx 语法的支持。

    • 转换插件
      转换插件就是社区里常说的 Babel 插件,负责转换 AST 节点。
      在介绍babel-traverse时提到,它负责遍历AST对象,每个AST节点会被访问到,等待转换,转换的部分,由"转换插件"负责。
      转换插件会提供一个叫做"Visitor"的对象,该对象的 Key 为节点名称,Value 部分提供进入该节点时、离开该节点时的回调函数,在回调函数里,可以对该节点进行一系列操作。
      “Visitor” 又称为 “访问者”。

// plugin 提供 visitor,在 visitor 中对 AST 节点操作
const visitor = {
    Program: {
        enter() {},
        exit() {},
    },

    CallExpression: {
        enter() {},
        exit() {},
    },

    NumberLiteral: {
        enter() {},
        exit() {},
    }
};
traverse(ast, visitor);

转换插件在Babel源码中,以 babel-plugin-transform 开头。

举个例子:

  • babel-plugin-transform-strict-mode

该插件拦截 Program 节点,也就是整个程序的根节点,添加 "use strict"指令。

visitor 节点值为函数时,是 enter 回调的快捷方式。

{
    name: "transform-strict-mode",

    visitor: {
      Program(path) {
        const { node } = path;

        for (const directive of node.directives) {
          if (directive.value.value === "use strict") return;
        }

        path.unshiftContainer(
          "directives",
          t.directive(t.directiveLiteral("use strict")),
        );
      },
    },
  };
}
  • babel-plugin-transform-object-assign

该插件负责拦截函数调用表达式节点 CallExpression,将 Object.assign 转为 extends 写法。

{
    name: "transform-object-assign",

    visitor: {
      CallExpression(path, file) {
        if (path.get("callee").matchesPattern("Object.assign")) {
          path.node.callee = file.addHelper("extends");
        }
      },
    },
}
  • Presets

Babel 插件的功能是细粒度的,大部分插件承担了单一功能。

而在实际开发过程中,往往需要支持对各类语法的支持。此时,有两种做法:

A. 需要支持哪些特性,就分别引入支持该特性的插件

B. 直接引入一个插件集合,涵盖所需的各类插件功能

很显然,第一种做法是相对麻烦的。针对第二种做法,Babel提供了插件集 preset。

preset 在 Babel 源码中,以 babel-preset 开头。

例如,Babel 已经提供了几种常用的 preset 供开发者使用:

  1. babel-preset-env
  2. babel-preset-react
  3. babel-preset-flow
  4. babel-preset-typescript
  • 插件运行顺序 Babel 配置项中,plugins 和 presets 均以数组的形式配置,执行时有先后顺序。

    • plugins 在 presets之前运行

    • plugins 按照数组正序执行

    • presets 按照数组倒序执行

08.jpg

工具模块

工具模块提供 Babel 相关模块所需的各类工具,以下一一简要介绍:

  • babel-core
    babel-core 对外提供了 Babel 多项功能的 API,如转译文件、转译代码、创建/获取配置等,在 Babel 官方文档介绍的比较详细,不再赘述。
    值得注意的是,转译类的 API 均提供了同步和异步版本,如 transformSync/transfomAsync、parseSync/parseAsync。

  • babel-cli
    Babel 的命令行工具,可以直接转译文件夹/文件,它也提供了很多配置项做其他工作,官方文档介绍的比较详细,感兴趣的同学可以去 Babel 官网查看详细配置。

  • babel-standalone
    Babel 对外服务的很多包是基于 node 环境下使用的,babel-standalone 提供了浏览器下转译的方案。
    babel-standalone 内置了所有 Babel 插件,所以体积还是比较大的,而且在浏览器端转译需要时间,比较适合开发、学习使用,不适合在生产环境使用。

举个例子:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>test babel-standalone</title>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const arr = [1, 2, 3];
      console.log(...arr);
</script>
  </head>
  <body></body>
</html>

在浏览器运行该 html,可以看到,页面结构变成了:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>test babel-standalone</title>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const arr = [1, 2, 3];
      console.log(...arr);
</script>
    <script>
      "use strict";
      var _console;
      var arr = [1, 2, 3];
      (_console = console).log.apply(_console, arr); //# sourceMappingURL=data:application/json;charset=utf-8;base64...
</script>
  </head>
  <body></body>
</html>
  • babel-node
    提供在命令行执行高级语法的环境。

例如:

// index.js 里可以使用高级语法     
babel-node -e index.js

index.js 文件以及被其引入的其他文件均可以使用高级语法了。和 babel-cli 不同的是,babel-cli 只负责转换,不在 node 运行时执行;babel-node 会在 node 运行时执行转换,不适合生产环境使用。

  • babel-register

在源文件中,引入babel-register,如 index.js:

index.js

require('babel-register');     
require('./run');

run.js

import fs from 'fs';     
console.log(fs);

执行 node index 时,run.js 就不需要被转码了。

babel-register 通过拦截 node require 方法,为 node 运行时引入了 Babel 的转译能力。

  • babel-loader

babel-loader 是利用 babel-core 的 API 封装的 webpack loader,用于 webpack 构建过程。

  • babel-types

babel-types 是一个非常强大的工具集合,它集成了节点校验、增删改查等功能,是 Babel 核心模块开发、插件开发等场景下不可或缺的工具。

例如:

const t = require('@babel/types');
const binaryExpression = t.binaryExpression('+', t.numericLiteral(1), t.numericLiteral(2));
  • babel-template

模板引擎,负责将代码字符串转为 AST 节点对象。

    import { smart as template } from '@babel/template';
    import generate from '@babel/generator';
    import * as t from '@babel/types';

    const buildRequire = template(      var %%importName%% = require(%%source%%);    );

    const ast = buildRequire({
        importName: t.identifier('myModule'),
        source: t.stringLiteral("my-module"),
    });

    const code = generate(ast).code

    console.log(code)

运行结果:

var myModule = require("my-module");
  • babel-code-frame

负责打印出错的代码位置,例如:

const { codeFrameColumns } = require('@babel/code-frame');

const testCode = `
class Run {
    constructor() {}
}
`;

const location = {
    start: {
        line: 2,
        column: 2,
    }
};

const result = codeFrameColumns(testCode, location);

console.log(result);
  1 | class Run {
> 2 |     constructor() {}
    |  ^
  3 | }
  4 |
  • babel-highlight

向控制台输出有颜色的代码片段。该工具可以识别 JavaScript 中的操作符号、标识符、保留字等类型的词法单元,并在终端环境下显示不同的颜色。

运行时相关模块

Babel 配合其插件可以对静态代码进行转译,但有一些遗漏点:

  • 对于运行时涉及的一些高版本 API,并没有提供兼容目标环境的 Polyfill。

  • 转译产物代码可能有些臃肿。

为此,运行时模块(runtime)关注的是转译产物的运行时环境,对运行时提供 API polyfill、代码优化等,该模块涉及几个子包:

  • babel-preset-env

  • babel-plugin-transform-runtime

  • babel-runtime

  • babel-runtime-corejs2/3

  • core-js

接下来以案例解释 runtime 模块的作用。

源码文件 index.js 的内容:

const a = 1; // const 为语法部分
class Base {} // class 为语法部分
new Promise() // Promise 为 API 部分

这段源码包含了语法和 API 部分:

  • const、class 为语法部分

  • Promise 为 API 部分

如果希望这段源码转为 ES5 版本,使构建产物可以运行在不支持 ES6 和 Promise 的环境里,该怎么做呢?

用 babel 命令行执行转译,其中源文件为 index.js,转译产物文件为 index-compiled.js。

npx babel index.js --out-file index-compiled.js

需要配置.babelrc 帮助 Babel 完成语法和 API 部分的转译:

.babelrc:

{
    "presets": [
        [ 
            "@babel/preset-env"
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3
            }
        ]
    ]
}

简要解释下该配置的原理:

  • babel-preset-env 可以完成语法部分转译,即 const 转译为var
    但构建产物中,有些辅助代码如 _classCallCheck 是以硬编码的形式直接写入转译产物的:
"use strict";

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

    var a = 1;

    var Base = function Base() {
        _classCallCheck(this, Base);
    };

    new Promise();

这样的后果就是构建产物比较臃肿。

  • babel-plugin-transform-runtime 可以将上述 _classCallCheck 置于通用包中,以引用的形式写入转译产物:
"use strict";

    var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

    var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

    var a = 1;

    var Base = function Base() {
        (0, _classCallCheck2["default"])(this, Base);
    };

    new Promise();
  • babel-plugin-transform-runtime 的配置参数 corejs 用于转译 API 部分,如 Promsie
    "use strict";

    var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

    var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

    var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));

    var a = 1;

    var Base = function Base() {
        (0, _classCallCheck2["default"])(this, Base);
    };

    new _promise"default";

Babel 转译过程的运行时优化是一个繁琐的过程,为此将单独用一章讲解运行时优化,感兴趣的同学可以直接阅读 “Babel Runtime” 章节详细了解。

三、标准化

Babel 生态涉及的一些标准化组织。无论是 JavaScript、HTML、DOM、URL 等领域,均需要统一的标准,才能在不同的运行环境下有统一的表现。Babel 转译也需要遵循这些标准,包括 ECMAScript、web标准等。

ECMAScript

JavaScript诞生

1995 年,JavaScript 的第一个版本发布。用时间线的方式描述 JavaScript 的诞生过程会更清晰:

09.jpg

ECMAScript发布

1996 年,微软模仿 JavaScript 实现了 JScript 并内置在 IE3.0,随后,Netscape 公司着手推动 JavaScript 标准化。

这里涉及几个组织:

  • Ecma International
    Ecma International 是一家国际性会员制度的信息和电信标准组织。1994年之前,名为欧洲计算机制造商协会(European Computer Manufacturers Association)。因为计算机的国际化,组织的标准牵涉到很多其他国家,因此组织决定改名表明其国际性。
    Ecma International 的任务包括与有关组织合作开发通信技术和消费电子标准、鼓励准确的标准落实、和标准文件与相关技术报告的出版。
    Ecma International 负责多个国际标准的制定:

    • CD-ROM 格式(之后被国际标准化组织批准为ISO 9660)

    • C# 语言规范

    • C++/CLI 语言规范

    • 通用语言架构(CLI)

    • Eiffel 语言

    • 电子产品环境化设计要素

    • Universal 3D 标准

    • OOXML

    • Dart 语言规范

    • ECMAScript 语言规范(以 JavaScript 为基础)ECMA-262
      其中就包括 JavaScript 标准语言规范 ECMAScript。
      cma International 拥有 ECMAScript 的商标。

  • ECMA TC39
    「TC39」全称「Technical Committee 39」译为「第 39 号技术委员会」,是 Ecma International 组织架构中的一部分。
    TC39 负责迭代和发展 ECMAScript,它的成员由各个主流浏览器厂商的代表组成,通常每年召开约 6 次会议来讨论未决提案的进展情况,会议的每一项决议必须得到大部分人的赞同,并且没有人强烈反对才可以通过。
    TC39 负责:

    • 维护和更新 ECMAScript 语言标准

    • 识别、开发、维护 ECMAScript 的扩展功能库

    • 开发测试套件

    • 为 ISO/IEC JTC 1 提供标准

    • 评估和考虑新添加的标准

  • ISO

国际标准化组织(英语: International Organization for Standardization,简称: ISO)成立于 1947 年 2 月 23 日,制定全世界工商业国际标准的国际标准建立机构。

ISO 的国际标准以数字表示,例如: “ISO 11180:1993” 的 “11180” 是标准号码,而 “1993” 是出版年份。

ISO/IEC JTC 1 是国际标准化组织和国际电工委员会联合技术委员会。其目的是开发、维护和促进信息技术以及信息和通信技术领域的标准。JTC 1 负责了许多关键的 IT 标准,从 MPEG 视频格式到 C++ 编程语言。

10.jpg

  • ECMAScript 发展过程中的关键节点

11.jpg

ECMAScript 各版本

ECMAScript 经历了多个版本,每个版本有自己的特点,简单列举如下:

12.jpg

13.jpg

ECMAScript 迭代过程

一个 ECMAScript 标准的制作过程,包含了 Stage 0 到 Stage 4 共 5 个阶段,每个阶段提交至下一阶段都需要 TC39 审批通过。

14.jpg

15.jpg

特性进入 Stage-4 后,才有可能被加入标准中,还需要 ECMA General Assembly 表决通过才能进入下一次的 ECMAScript 标准中。

如何阅读 ECMAScript

ECMAScript 文档结构

ECMAScript 的规格,可以在 ECMA 国际标准组织的官方网站免费下载和在线阅读。

查看ECMAScript 不同版本的地址:++https://ecma-international.org/publications-and-standards/standards/ecma-262/++

截至 2023年底,已发布的版本如下:

  • ECMA-262 5.1 edition, June 2011

(https://262.ecma-international.org/5.1/index.html)

  • ECMA-262, 6th edition, June 2015

(https://262.ecma-international.org/6.0/index.html)

  • ECMA-262, 7th edition, June 2016

(https://262.ecma-international.org/7.0/index.html)

  • ECMA-262, 8th edition, June 2017

(https://262.ecma-international.org/8.0/index.html)

  • ECMA-262, 9th edition, June 2018

(https://262.ecma-international.org/9.0/index.html)

  • ECMA-262, 10th edition, June 2019

(https://262.ecma-international.org/10.0/index.html)

  • ECMA-262, 11th edition, June 2020

(https://262.ecma-international.org/11.0/index.html)

  • ECMA-262, 12th edition, June 2021

(https://262.ecma-international.org/12.0/index.html)

  • ECMA-262, 13th edition, June 2022

(https://262.ecma-international.org/13.0/index.html)

  • ECMA-262, 14th edition, June 2023

(https://262.ecma-international.org/14.0/index.html)

每个版本有独立的网址,格式为: https://262.ecma-international.org/{version}/,比如 ECMAScript 14.0 版本的网址为 https://262.ecma-international.org/14.0/。

从章节数量上,ECMAScript 6.0、ECMAScript 7.0 有 26 章,之后的版本有 27-29 章,虽然章节数量不同,规格章节的分布是保持一定规律的,以 ECMAScript 11.0 版本为例:

  • Introduction: 介绍部分

该章节简要描述了: JavaScript 和 ECMAScript 的发展历史、不同 ECMAScript 规格的主要更新内容。

  • 第 1 章到第 3 章: 描述了规格文件本身,而非语言

    • 第 1 章用一句话描述了该规格的描述范围。

    • 第 2 章描述了基于规格的"实现"的一致性要求:

      • "实现"必须支持规格中描述的所有类型、值、对象、属性、函数以及程序的语法和语义

      • "实现"必须按照 Unicode 标准和 ISO/IEC 10646 的最新版本处理文本输入

      • "实现"如果提供了应用程序编程接口(API),那么该 API 需要适应不同的人文语言和国家差异,且必须实现最新版本的 ECMA-402 所定义的与本规范相兼容的接口

      • "实现"可以支持该规格中没有提及的类型、值、对象、属性、函数、正则表达式语法以及其他编程写法

      • "实现"不能实现该规格中禁止的写法

  • 第 3 章描述了该规格的一些参考资料:

    • ISO/IEC 10646

    • ECMA-402

    • EMCA-404 JSON 数据交换格式规范

  • 第 4 章: 对这门语言总体设计的描述。

  • 第 5 章到第 8 章: 语言宏观层面的描述。

  • 第 6 章介绍数据类型。

  • 第 7 章介绍语言内部用到的抽象操作。

  • 第 8 章介绍代码如何运行。

  • 第 9 章到第 27 章: 介绍具体的语法。

一般而言,除非写编译器,开发者无需阅读 ECMAScript 的规格,规格的内容非常多,如无必要也无需通读。只是在遇到一些奇怪的问题时,阅读官方规格,是最稳妥的办法。

通过阅读规格解决一些问题(以ECMAScript 11.0为例)

  • 识别关键词和保留字,并高亮
    Babel 工具集中的 babel-highlight,可以实现在终端对代码块中的目标字符单元显示不同的颜色。这里需要识别不同字符单元的类型,如关键字、保留字、标识符、数字、字符串等。
    标识符、数字、字符串都很好理解和识别,但哪些字符应该被识别为关键字、保留字,而不是标识符呢?
    此时可以阅读 ECMAScript 规格了,ECMAScript 11.0 规格的 11.6.2 节介绍了关键词和保留字列表。

    • 关键词(keywords)
      关键词首先是标识符,同时有语义,包括 if、while、async、await…,个别关键词是可以用作变量名的。

    • 保留字(reserved word)
      保留字首先是标识符,但不能用作变量名。
      部分关键词是保留字,但部分不是: if、while是保留字;await只有在 async方法和模块中才是保留字;async不是保留字,它可以作为普通的变量名使用。

    • 保留字列表

await
break
case
catch
class
const
continue
debugger
default
delete
do
else
enum
export
extends
false
finally
for
function
if
import
in
instanceof
new
null
returns
uper
switch
this
throw
true
try
typeof
var
void
while
with
yield

读完上述规格,也就知道哪些字符单元是需要识别为保留字与关键词,并高亮的了。

  • 识别全局对象,并高亮
    继续使用 babel-highlight 实现代码块中的全局对象高亮,那么,我们需要知道哪些是规格中描述的全局变量。
    规格的 18 章介绍了全局对象,通过该章的描述,可以知道:

    • 全局属性
      全局属性有: globalThis、Infinity、NaN、undefined。

      • 全局方法
        全局方法有: eval(x)、isFinite、isNaN、parseFloat、parseInt、decodeURIComponent、encodeURIComponent 等。

      • 全局构造函数
        全局的构造函数有:

Array
ArrayBuffer
BigInt
BigInt64Array
BigUnit64Array
Boolean
DataView
Date
Error
EvalError
Float32Array
Float64Array
Function
Int8Array
Int16Array
Int32Array
Map
Number
Object
Promise
Proxy
RangeError
ReferenceError
RegExp
Set
SharedArrayBuffer
String
Symbol
SyntaxError
TypeError
Uint8Array
Uint8ClampedArray
Uint16Array
Uint32Array
URIError
WeakMap
WeakSet
  • 其他的全局属性
    Atomics、JSON、Math、Reflect。
    很显然,当字符单元的名称是上述名称中的一员时,我们可以对其进行高亮处理了(若上下文中无重新定义的同名变量)。

  • 自定义 Error
    babel-loader 自身维护了私有的 LoaderError 对象,该对象继承自原生 Error 类,并且订制了部分实例属性。代码如下:


class LoaderError extends Error {
    constructor(err) {
        super();

        const { name, message, codeFrame, hideStack } = format(err);

        this.name = "BabelLoaderError";

        this.message = ${name ? ${name}: ` : ""}${message}\n\n${codeFrame}\n`;

        this.hideStack = hideStack;

        Error.captureStackTrace(this, this.constructor);
    }
}

可以看到,babel-loader 自定义了错误实例的 name、message、hideStack 属性,那么,问题是,原生的 Error 类有哪些属性和方法,哪些是开发者可以自定义的呢?

规格的 19.5 章节,详细介绍了 Error 的各类规范:

  • Error 作为函数被调用时(Error(…)),表现和 new Error(…) 一致,均会创建并返回 Error 的新实例

  • Error 可以被继承,比如通过 extends 的方式,子类必须提供 constructor 方法,且该方法内必须提供 super() 调用

  • Error 构造函数必须有 prototype 属性

    • Error.prototype 属性需有以下属性:

      • Error.prototype.constructor: 指向构造函数

      • Error.prototype.message: 描述错误信息,默认是空字符串

      • Error.prototype.name: 描述错误名称,默认值是 Error

从 LoaderError 的源码可以看到,LoaderError 做了以下几件事情:

  • LoaderError 继承自 Error

  • 实例自定义了 name、message 属性,明确 babel-loader 的信息

  • 实例自定义的 hideStack 属性是非标准属性,用于 babel-loader 内部

web标准

是在解决 API Polyfil 的时候,Babel 配合使用的 core-js 除了提供 ECMAScript 标准下的 JavaScript API 实现,也提供了 DOM/URL 等实现。而 DOM/URL 所属的 web 标准,由 W3C/WHATWG 制定。

16.jpg

经过多年发展,WHATWG 和 W3C 目前是合作关系,其中,WHATWG 维护 HTML 和 DOM 标准,W3C 使用 WHATWG 存储库中的 HTML 和 DOM 标准描述,W3C 在 HTML 部分的工作集中在 XHTML/XML 上。

17.jpg

总结

本文介绍了 Babel 的概述/微内核架构/ECMAScript标准化方面的设计思想和部分实现原理。

上述内容其实在很早之前就已经成型了,笔者也查看了Babel最近的迭代内容,发现并没有太大的变化。至于代码转译领域,目前是Babel还是其他工具哪个更有优势,不在本文的讨论范围内。除了比较社区哪些工具更好而言,“Babel的设计思路、其与标准规范是怎么配合的”等也是很值得学习的地方,也是这篇文章的产生背景。

希望本文对你有所帮助!

*文 / hoperyy

本文属得物技术原创,更多精彩文章请看:得物技术

未经得物技术许可严禁转载,否则依法追究法律责任!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2107065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式4629张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4629 标注数量(xml文件个数)&#xff1a;4629 标注数量(txt文件个数)&#xff1a;4629 标注…

CDO的核心价值与角色深化

随着数字化浪潮席卷各行各业&#xff0c;首席数据官&#xff08;CDO&#xff09;这一角色日益显得重要&#xff0c;成为企业战略规划的核心。 他们的主要任务是深入挖掘数据潜能&#xff0c;通过精确的数据洞察为企业的成长和运营优化提供坚实的数据支持和策略指导。 首席数据…

C语言操作符详解2(含结构体、整型提升、算术转换)

文章目录 一、逗号表达式二、下标访问操作符[]、函数调用操作符()1.下标访问操作符[]2.函数调用操作符 三、结构成员访问操作符1.结构体2.结构的声明3.结构体变量的定义和初始化&#xff08;1&#xff09;结构体变量的定义&#xff08;2&#xff09;结构体变量的初始化 4.结构成…

数据结构(三)——双向链表,循环链表,内核链表,栈和队列

双链表 产生原因&#xff1a;单链表只有一个指向后继的指针&#xff0c;如果要访问某节点的前驱结点&#xff0c;只能从头遍历&#xff0c;也就是访问后继节点的时间复杂度为1&#xff0c;访问前驱结点的时间复杂度为n。 而引入双链表使得在插入、删除的…

C语言代码练习

今日练习&#xff1a; 34、通过指向结构体变量的指针变量变量输出结构体变量中的信息。 35、建立一个简单链表&#xff0c;它由3个学生数据的结点组成&#xff0c;要求输出各结点中的数据。36、从键盘输入一些字符&#xff0c;逐个把他们送到磁盘上去&#xff0c;直到用户输入一…

【电机控制】永磁同步电机FOC控制—电流环PI参数整定

0. 前言 在了解电流环PI参数整定之前我们需要有以下几点基础知识&#xff1a; 1、了解什么是传递函数、传递函数的零点和极点。 2、了解极点对系统稳定性的影响。 有基础的小伙伴可以跳着看。 1. 传递函数与零点极点 1.1 传递函数 系统传递函数G(s)的定义是&#xff1a;在指零…

Pytorch – YOLOv9自定义资料训练

本篇将讲解目前最新推出的YOLOv9搭配Roboflow进行自定义资料标注训练流程&#xff0c;透过Colab上进行实作说明&#xff0c;使大家能够容易的了解YOLOv9的使用。 ►YOLO框架下载与导入 ►Roboflow的资料收集与标注 进入Roboflow官网&#xff0c;点选右上Sign up注册自己的帐号…

网站设计公司设计费用

网站设计公司的设计费用往往是一个让人头疼的问题。设计费用的高低涉及到多个因素&#xff0c;包括公司规模、项目复杂性、设计师经验等。本文将深入探讨网站设计公司的设计费用&#xff0c;并提供一些建议&#xff0c;帮助您在有限预算下实现最佳性价比。 1. 项目规模与复杂性…

GitHub 上 Stars 数量最多的 8 个开源 CRUD 项目

继续我们的 GitHub Star 系列&#xff01;这是本系列的第四篇文章&#xff0c;之前的内容&#xff1a; GitHub Star 数量前 12 的开源无代码工具GitHub Star 数量前 15 的开源低代码项目GitHub Star 数量前 11 的开源内部工具 本期我们来盘点 CRUD 项目。在软件开发中&#x…

爬虫练习(猫眼电影解密)

问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后&#xff0c;数据全是加密后的。所以我们需要想办法破解加密&#xff0c;拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的&#xff…

[Linux] 操作系统 入门详解

标题&#xff1a;[Linux] 操作系统 水墨不写bug 目录 一、冯 . 诺依曼体系结构 1.冯诺依曼体系结构简介 2.对冯诺依曼体系结构的理解 二、操作系统定位 1.为什么需要操作系统&#xff1f; 2.操作系统是什么&#xff1f; 三、系统调用和库函数 正文开始&#xff1a; …

可交互、会学习、自成长机器人——李德毅院士

在以“农业无人农场”为主题的中国工程科技论坛上&#xff0c;中国工程院院士、欧亚科学院院士、中国人工智能学会和中国指挥与控制学会名誉理事长&#xff0c;中科原动力首席科学家李德毅院士应邀做题为《机器具身交互智能》的演讲。李德毅院士表示&#xff0c;智能机器不但把…

CentOS7单机环境安装k8s集群

目录 1、环境准备 2、安装依赖工具 3、配置 Kubernetes 的国内 Yum 源 4. 安装 Kubernetes 组件 5、初始化 Kubernetes 集群 1. 容器运行时没有正常运行 1.1. 可能的原因 1.2. 解决办法 2. 初始化拉取镜像卡住 2.1. 使用国内的镜像源&#xff08;无法解决问题&#x…

机器学习之监督学习(二)二元逻辑回归

机器学习之监督学习&#xff08;二&#xff09;逻辑回归(二元分类问题&#xff09; 1. 分类 classification2.二元分类逻辑回归 binary-classified logistic regression模块1: sigmoid 激活函数 sigmoid function模型公式模块2: 决策边界 decision boundary代价函数梯度下降欠拟…

【Redis】Redis 主从复制原理与配置详解:解决单点故障与性能瓶颈的最佳方案

目录 主从复制配置建立复制断开复制安全性只读传输延迟 拓扑⼀主⼀从结构一主多从结构树形主从结构 原理复制过程数据同步 psync全量复制部分复制实时复制 小结 主从复制 这部分相关操作不需要记忆!!! 后续⼯作中如果⽤到了能查到即可. 重点理解流程和原理. 单点问题&#xff1…

onvif应用--IPC鉴权(认证)

一、鉴权原理 1&#xff09;onvif的用户验证&#xff0c;是基于WS_UsernameToken&#xff0c;所谓的WS_UsernameToken加密&#xff0c;就是将用户名、密码、Nonce、Created都包含在了header里面 参数 意义 username待认证的用户名Nonce客户端随机产生的字符串Created请求认证…

arXiv风评被害?

arXiv “风评被害”&#xff1f; arXiv是一个在学术界具有重要影响力的开放预印本论文网站&#xff0c;自三十多年前创立以来&#xff0c;它已经成为了物理学、计算机科学、统计学等科学论文最重要的发布平台之一&#xff0c;同时也是众多科研人员分享和交流研究成果的重要渠道…

单片机工程师:创新与挑战之路

摘要&#xff1a;本文全面深入地探讨了单片机工程师这一职业角色。详细阐述了单片机工程师的职责范围、所需技能&#xff0c;包括硬件设计、软件编程、调试与测试等方面。分析了单片机在不同领域的应用&#xff0c;如工业控制、消费电子、智能家居等。同时&#xff0c;探讨了单…

【计算机组成原理】计算机系统的基本组成

文章目录 计算机硬件的基本组成早期的冯诺依曼机冯诺依曼结构冯诺依曼机的特点 现代计算机现代计算机的结构 各硬件的工作原理主存储器主存储器的基本组成 运算器运算器的基本组成 控制器控制器的基本组成 计算机软件 计算机硬件的基本组成 早期的冯诺依曼机 冯诺依曼在研究 …

图纸文件怎么加密?2024六款图纸加密软件推荐,个个好用不踩雷!

想象一下&#xff0c;公司的设计图纸被无意间泄露&#xff0c;结果对手提前推出了相似的产品。为了避免这种令人头疼的情况发生&#xff0c;图纸加密就显得尤为重要。 别担心&#xff0c;今天我们就带你了解2024年六款超好用的图纸加密软件&#xff0c;让你的图纸“安全感”爆…