基于原型与基于类的继承

新手上路,请多包涵

在 JavaScript 中,每个对象同时是一个实例和一个类。要进行继承,您可以使用任何对象实例作为原型。

在 Python、C++ 等中,有类和实例作为单独的概念。为了进行继承,您必须使用基类创建一个新类,然后可以使用它来生成派生实例。

为什么 JavaScript 朝这个方向发展(基于原型的面向对象)?相对于传统的基于类的 OO,基于原型的 OO 有哪些优势(和劣势)?

原文由 Stefano Borini 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 381
2 个回答

这里有大约一百个术语问题,主要是围绕某人(而不是你)建立的,试图让他们的想法听起来像最好的。

所有面向对象的语言都需要能够处理几个概念:

  1. 数据的封装以及对数据的相关操作,通常称为数据成员和成员函数,或称为数据和方法等。
  2. 继承,能够说这些对象除了这些变化之外就像其他对象集一样
  3. 多态性(“多种形状”),其中对象自行决定要运行哪些方法,以便您可以依赖语言来正确路由您的请求。

现在,就比较而言:

首先是整个“类”与“原型”的问题。这个想法最初起源于 Simula,其中使用基于类的方法,每个类代表一组共享相同状态空间(读取“可能值”)和相同操作的对象,从而形成一个等价类。如果你回顾一下 Smalltalk,因为你可以打开一个类并添加方法,这实际上与你在 Javascript 中所做的相同。

后来的 OO 语言希望能够使用静态类型检查,所以我们有了在编译时设置固定类的概念。在公开课版本中,你有更多的灵活性;在较新的版本中,您可以在编译器中检查某些类型的正确性,否则需要进行测试。

在“基于类”的语言中,复制发生在编译时。在原型语言中,操作存储在原型数据结构中,在运行时复制和修改。但是,从抽象上讲,类仍然是共享相同状态空间和方法的所有对象的等价类。当您向原型添加一个方法时,实际上是在创建一个新的等价类的元素。

现在,为什么要这样做?主要是因为它在运行时提供了一种简单、合乎逻辑、优雅的机制。现在,要创建一个新对象 创建一个新类,您只需执行深复制,复制所有数据和原型数据结构。然后,您或多或少地免费获得了继承和多态性:方法查找 总是 包括按名称向字典询问方法实现。

最终出现在 Javascript/ECMA 脚本中的原因基本上是因为当我们在 10 年前开始使用它时,我们正在处理功能不那么强大的计算机和不那么复杂的浏览器。选择基于原型的方法意味着解释器可以非常简单,同时保留面向对象的理想属性。

原文由 Charlie Martin 发布,翻译遵循 CC BY-SA 3.0 许可协议

可以在论文 Self: The Power of Simplicity 中找到一个比较,它稍微偏向于基于原型的方法。该论文提出了以下支持原型的论点:

通过复制创建。从原型创建新对象是通过一个简单的操作完成的,复制,用一个简单的生物学比喻,克隆。从类创建新对象是通过实例化完成的,实例化包括类中格式信息的解释。实例化类似于根据计划建造房屋。复制对我们来说是一个比实例化更简单的隐喻。

预先存在的模块的示例。原型比类更具体,因为它们是对象的示例而不是格式和初始化的描述。这些示例可以通过使模块更易于理解来帮助用户重用模块。基于原型的系统允许用户检查一个典型的代表,而不是要求他理解其描述。

支持独一无二的对象。 Self 提供了一个框架,可以轻松地包含具有自己行为的独一无二的对象。由于每个对象都有命名槽,并且槽可以保存状态或行为,因此任何对象都可以有唯一的槽或行为。基于类的系统是为有许多具有相同行为的对象的情况而设计的。一个对象拥有自己独特的行为没有语言支持,而且创建一个保证只有一个实例的类是很尴尬的[ 想想单例模式]。自我没有这些缺点。任何对象都可以用它自己的行为来定制。一个独特的对象可以拥有独特的行为,并且不需要一个单独的“实例”。

消除元回归。基于类的系统中的任何对象都不能自给自足;需要另一个对象(它的类)来表达它的结构和行为。这导致概念上无限的元回归:a point 是类 Point 的实例,它是元类 Point 的实例元元类 Point ,无穷无尽。另一方面,在基于原型的系统中,一个对象可以包含它自己的行为;不需要其他物体来为它注入生命。原型消除元回归。

Self 可能是第一个实现原型的语言(它还开创了其他有趣的技术,如 JIT,后来进入了 JVM),因此阅读 其他 Self 论文 也应该很有启发性。

原文由 Vijay Mathew 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题