JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换 (2)

如上所述,如果希望继承基类的的所有特性,那么每次都需要执行这个复杂的逻辑。当创建多个类时,将逻辑封装在可重用函数中是有意义的。这就是开发人员最初解决基于类继承的方法——通过使用不同的库来模拟它。

这些解决方案越来越流行,造成了 JS 中明显缺少了一些类型的现象。这就是为什么在 ECMAScript 2015 的第一个主要版本中引入了类,继承的新语法。

类的转换

当 ES6 或 ECMAScript 2015 中的新特性被提出时,JavaScript 开发人员不能等待所有引擎和浏览器都开始支持它们。为实现浏览器能够支持新的特性一个好方法是通过 转换 (Transpiling) ,它允许将 ECMAScript 2015 中编写的代码转换成任何浏览器都能理解的 JavaScript 代码,当然也包括使用基于类的继承编写类的转换功能。

JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换

Babel

最流行的 JavaScript 编译器之一就是 Babel,宏观来说,它分3个阶段运行代码:解析(parsing),转译(transforming),生成(generation),来看看它是如何转换的:

class Component { constructor(content) { this.content = content; } render() { console.log(this.content) } } const component = new Component('SessionStack'); component.render();

以下是 Babel 转换后的样式:

var Component = function () { function Component(content) { _classCallCheck(this, Component); this.content = content; } _createClass(Component, [{ key: 'render', value: function render() { console.log(this.content); } }]); return Component; }();

如上所见,转换后的代码就可在任何浏览器执行了。 此外,还添加了一些功能, 这些是 Babel 标准库的一部分。

_classCallCheck 和_createClass 作为函数包含在编译文件中。

_classCallCheck 函数的作用在于确保构造方法永远不会作为函数被调用,它会评估函数的上下文是否为 Component对象的实例,以此确定是否需要抛出异常。

_createClass 用于处理创建对象属性,函数支持传入构造函数与需定义的键值对属性数组。函数判断传入的参数(普通方法/静态方法)是否为空对应到不同的处理流程上。

为了探究继承的实现原理,分析继承的 Component 的 InputField 类。。

class InputField extends Component { constructor(value) { const content = `<input type="text" value="${value}" />`; super(content); } }

使用 Babel 处理上述代码,得到如下代码:

var InputField = function (_Component) { _inherits(InputField, _Component); function InputField(value) { _classCallCheck(this, InputField); var content = '<input type="text" value="' + value + '" />'; return _possibleConstructorReturn(this, (InputField.__proto__ || Object.getPrototypeOf(InputField)).call(this, content)); } return InputField; }(Component);

在本例中, Babel 创建了 _inherits 函数帮助实现继承。

以 ES6 转 ES5 为例,具体过程:

编写ES6代码

babylon 进行解析

解析得到 AST

plugin 用 babel-traverse 对 AST 树进行遍历转译

得到新的 AST树

babel-generator 通过 AST 树生成 ES5 代码

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。

Babel 中的抽象语法树

AST 包含多个节点,且每个节点只有一个父节点。 在 Babel 中,每个形状树的节点包含可视化类型、位置、在树中的连接等信息。 有不同类型的节点,如 string,numbers,null等,还有用于流控制(if)和循环(for,while)的语句节点。 并且还有一种特殊类型的节点用于类。它是基节点类的一个子节点,通过添加字段来扩展它,以存储对基类的引用和作为单独节点的类的主体。

把下面的代码片段转换成一个抽象语法树:

class Component { constructor(content) { this.content = content; } render() { console.log(this.content) } }

下面是以下代码片段的抽象语法树:

JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换

Babel 的三个主要处理步骤分别是: 解析(parse),转换 (transform),生成 (generate)。

解析

将代码解析成抽象语法树(AST),每个js引擎(比如Chrome浏览器中的V8引擎)都有自己的AST解析器,而Babel是通过 Babylon 实现的。在解析过程中有两个阶段: 词法分析 和 语法分析 ,词法分析阶段把字符串形式的代码转换为 令牌 (tokens)流,令牌类似于AST中节点;而语法分析阶段则会把一个令牌流转换成 AST的形式,同时这个阶段会把令牌中的信息转换成AST的表述结构。

转换

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdypy.html