这个答案应该被视为填补其他答案中缺失的要点的扩展。个人经验和基准测试都被纳入考虑。
就我的经验而言,无论方法是私有的还是公有的,我都会虔诚地使用构造函数来构建我的对象。主要原因是当我开始时,这是对我来说最容易立即实现的方法,所以它并不是一种特殊的偏好。可能仅仅是因为我喜欢可见的封装,而原型有点脱离实体。我的私有方法也将在作用域中分配为变量。虽然这是我的习惯,并且使事情保持得很好,但并不总是最好的习惯,有时我也会遇到困难。除了高度动态自组装的奇怪场景和代码布局之外,如果性能是一个问题,它往往是较弱的方法。知道内部是私有的很有用,但是您可以通过正确的纪律采取其他手段来实现这一点。除非性能是一个严重的考虑因素,否则根据手头任务使用最好的方法。
使用原型继承和标记项目为私有的约定确实使调试更容易,因为您可以轻松地从控制台或调试器遍历对象图。另一方面,这种约定使混淆变得更加困难,并使其他人更容易将自己的脚本添加到您的站点上。这是私有范围方法变得流行的原因之一。它不是真正的安全性,而是增加了抵抗力。不幸的是,很多人仍然认为这是一种真正编写安全JavaScript的方式。由于调试器已经变得非常好,代码混淆取代了它的位置。如果您正在寻找在客户端过多的安全漏洞,则可能需要注意设计模式。
约定允许您轻松拥有受保护的属性。这可能是一种祝福,也可能是一种诅咒。它确实减轻了一些继承问题,因为它不那么限制。您仍然有冲突的风险或者考虑其他地方可能访问属性时的认知负荷增加。自组装对象可以让您做一些奇怪的事情,您可以解决许多继承问题,但它们可能是非传统的。我的模块倾向于具有丰富的内部结构,直到功能在其他地方(共享)需要时才会将其拆分出来,或者除非需要在外部使用。构造函数模式往往会导致创建自包含的复杂模块,而不是简单的零散对象。如果您想要这样做,那么没问题。否则,如果您想要更传统的OOP结构和布局,那么我可能建议按约定规范访问。在我的使用场景中,复杂的OOP并不经常被证明是合理的,而模块可以胜任。
这里的所有测试都是最小的。在实际使用中,模块很可能会更加复杂,使得击中比这里的测试所示的要大得多。通常情况下,私有变量具有多个方法对其进行操作,每个方法都会增加更多的开销,这是您在原型继承中不会遇到的。在大多数情况下,这并不重要,因为只有少数这样的对象实例浮动,尽管累积起来可能会增加。
有一个假设,即原型方法调用速度较慢,因为需要查找原型。这不是不公平的假设,我自己也做了同样的事情,直到我测试过它。实际上,这很复杂,一些测试表明这个方面是微不足道的。在
prototype.m = f
、
this.m = f
和
this.m = function...
之间,后者比前两者表现得更好,前两者的表现大致相同。如果仅仅原型查找就是一个重要问题,那么后两个函数将显著优于第一个函数。相反,至少在Canary中发生了一些奇怪的事情。可能根据成员进行了函数优化。许多性能考虑因素都会产生影响。您还需要考虑参数访问和变量访问的差异。
内存容量。这里没有很好地讨论。您可以预先做出的假设是,原型继承通常会更加节省内存,并
免责声明:
- 关于性能有很多讨论,但并不总是有一个永久正确的答案,因为使用场景和引擎会发生变化。始终进行分析,但也要以多种方式进行测量,因为分析并不总是准确可靠的。除非确实存在可证明的问题,否则避免将大量精力投入到优化中。
- 最好的方法可能是在自动化测试中包含敏感区域的性能检查,并在浏览器更新时运行。
- 记住有时电池寿命也很重要,而不仅仅是可感知的性能。最慢的解决方案可能在运行优化编译器后变得更快(例如,编译器可能比按约定标记为私有的属性更好地了解何时访问受限范围变量)。考虑后端,如node.js。这可能需要比您通常在浏览器上找到的更好的延迟和吞吐量。大多数人不需要担心这些事情,比如注册表单的验证,但此类事情可能会影响越来越多的不同场景。
- 在持久化结果之前,必须小心使用内存分配跟踪工具。在某些情况下,如果我没有返回和持久化数据,它将被完全优化掉,或者实例化/未引用之间的采样率不足,让我想知道如何将初始化并填充到一百万个数组注册为3.4KiB在分配配置文件中。
- 在现实世界中,在大多数情况下,真正优化应用程序的唯一方法是首先编写它,以便您可以对其进行测量。在任何给定的情况下,可能会涉及几十到数百个因素,甚至数千个因素。引擎还会执行可能导致非对称或非线性性能特征的操作。如果在构造函数中定义函数,则它们可能是箭头函数或传统函数,每个函数在某些情况下的行为都不同,而我对其他函数类型一无所知。类也不会在原型构造函数的性能方面表现相同,即使它们应该是等效的。您需要非常小心地处理基准测试。原型类可以以各种方式延迟初始化,特别是如果您也将属性原型化(建议不要这样做)。这意味着您可能会低估初始化成本并高估访问/属性突变成本。我还看到过渐进式优化的迹象。在这些情况下,我用相同的对象实例填充了一个大数组,随着实例数量的增加,对象似乎逐步优化为内存,直到剩余部分相同。这些优化也可能显著影响CPU性能。这些事情非常依赖于您编写的代码以及运行时发生的事情,例如对象数量,对象之间的差异等。