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

这是专门探索 JavaScript 及其所构建的组件的系列文章的第 15 篇。

如果你错过了前面的章节,可以在这里找到它们:

JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述!

JavaScript 是如何工作的:深入V8引擎&编写优化代码的5个技巧!

JavaScript 是如何工作的:内存管理+如何处理4个常见的内存泄漏 !

JavaScript 是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!

JavaScript 是如何工作的:深入探索 websocket 和HTTP/2与SSE +如何选择正确的路径!

JavaScript 是如何工作的:与 WebAssembly比较 及其使用场景 !

JavaScript 是如何工作的:Web Workers的构建块+ 5个使用他们的场景!

JavaScript 是如何工作的:Service Worker 的生命周期及使用场景!

JavaScript是如何工作的:使用 MutationObserver 跟踪 DOM 的变化!

JavaScript是如何工作的:深入网络层 + 如何优化性能和安全!

JavaScript是如何工作的:CSS 和 JS 动画底层原理及如何优化它们的性能!

JavaScript是如何工作的:解析、抽象语法树(AST)+ 提升编译速度5个技巧!

现在构建任何类型的软件项目最流行的方法这是使用类。在这篇文章中,探讨用 JavaScript 实现类的不同方法,以及如何构建类的结构。首先从深入研究原型工作原理,并分析在流行库中模拟基于类的继承的方法。 接下来是讲如何将新的语法转制为浏览器识别的语法,以及在 Babel 和 TypeScript 中使用它来引入ECMAScript 2015类的支持。最后,将以一些在 V8 中如何本机实现类的示例来结束本文。

概述

在 JavaScript 中,没有基本类型,创建的所有东西都是对象。例如,创建一个新字符串:

const name = "SessionStack";

接着在新创建的对象上调用不同的方法:

console.log(a.repeat(2)); // SessionStackSessionStack console.log(a.toLowerCase()); // sessionstack

与其他语言不同,在 JavaScript 中,字符串或数字的声明会自动创建一个封装值的对象,并提供不同的方法,甚至可以在基本类型上执行这些方法。

另一个有趣的事实是,数组等复杂类型也是对象。如果检查数组实例的类型,你将看到它是一个对象。列表中每个元素的索引只是对象中的属性。当通过数组中的索引访问一个元素时,实际上是访问了数组对象的一个 key 值,并得到 key 对应的值。从数据的存储方式看时,这两个定义是相同的:

let names = [“SessionStack”]; let names = { “0”: “SessionStack”, “length”: 1 }

因此,访问数组中的元素和对象的属性耗时是相同的。我(本文作者)通过多次的努力才发现这一点的。就是不久,我(本文作者)不得不对项目中的一段关键代码进行大规模优化。在尝试了所有简单的可选项之后,最后用数组替换了项目中使用的所有对象。理论上,访问数组中的元素比访问哈希映射中的键要快且对性能没有任何影响。在 JavaScript中,这两种操作都是作为访问哈希映射中的键来实现的,并且花费相同的时间。

使用原型模拟类

一般的想到对象时,首先想到的是类。我们大都习惯于根据类及其之间的关系来构建应用程序。尽管 JavaScript 中的对象无处不在,但该语言并不使用传统的基于类的继承,相反,它依赖于来实现。

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

在 JavaScript 中,每个对象通过原型连接着另一个对象。当尝试访问对象上的属性或方法时,首先从对象本身开始查找,如果没有找到任何内容,则在对象的原型中继续查找。

从一个简单的例子开始:

function Component(content) { this.content = content; } Component.prototype.render = function() { console.log(this.content); }

在 Component 的原型上添加 render 方法,因为希望 Component 的每个实例都能有 render 方法。Component 任何实例调用此方法时,首先将在实例本身中执行查找,如果没有,接着从它的原型中执行查找。

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

接着引入一个新的子类:

function InputField(value) { this.content = `<input type="text" value="${value}" />`; }

如果想要 InputField 继承 Component 并能够调用它的 render 方法,就需要更改它的原型。当对子类的实例调用 render方法时,不希望在它的空原型中查找,而应该从从 Component 上的原型查找:

InputField.prototype = Object.create(new Component());

通过这种方式,就可以在 Component 的原型中找到 render 方法。为了实现继承,需要将 InputField 的原型连接到 Component 的实例上,大多数库都使用 方法来实现这一点。

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

然而,这不是唯一一件事要做的,每次继承一个类,需要:

将子类的原型指向父类的实例。

在子类构造函数中调用的父构造函数,完成父构造函数中的初始化逻辑。

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

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