基于原型的继承 VS基于类的.class继承

prototype based vs. class based inheritance

提问人:Stefano Borini 提问时间:5/3/2009 最后编辑:MachavityStefano Borini 更新时间:12/12/2022 访问量:81418

问:

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

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

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

JavaScript OOP 继承 原型编程

评论

14赞 Aadit M Shah 6/2/2013
JavaScript 受到 Self 的影响,Self 是第一个具有原型继承的语言。当时,古典继承风靡一时,首先在《模拟》中引入。然而,古典继承太复杂了。然后 David Ungar 和 Randall Smith 在阅读 GEB 后顿悟了——“最具体的事件可以作为一类事件的一般例子。他们意识到面向对象编程不需要类。因此,Self 诞生了。要了解原型遗传如何比经典遗传更好,请阅读以下内容: stackoverflow.com/a/16872315/783743 =)
1赞 Alex 6/17/2016
@AaditMShah 什么/谁是?GEB
6赞 Aadit M Shah 6/19/2016
@Alex GEB 是道格拉斯·霍夫施塔特 (Douglas Hofstadter) 撰写的一本书。它是哥德尔·埃舍尔·巴赫(Gödel Escher Bach)的缩写。库尔特·哥德尔(Kurt Gödel)是一位数学家。埃舍尔是一位艺术家。巴赫是一位钢琴家。

答:

231赞 Charlie Martin 5/3/2009 #1

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

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

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

现在,就比较而言:

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

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

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

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

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

评论

1赞 Charlie Martin 5/3/2009
对了,那个段落读起来好像我不是这个意思吗?Dahl 和 Nyqvist 提出了“类”作为具有相同方法签名的事物的集合。
1赞 Charlie Martin 5/3/2009
这种变化是否更好?
2赞 Charlie Martin 5/3/2009
不,对不起,CLOS 来自 80 年代后期 dreamsongs.com/CLOS.html 1980 年的 Smalltalk en.wikipedia.org/wiki/Smalltalk 和 1967-68 年具有完全面向对象的 Simula en.wikipedia.org/wiki/Simula
3赞 Charlie Martin 5/3/2009
@Stephano,它们并不像所有那样截然不同:Python、Ruby、Smalltalk 使用字典进行方法查找,javascript 和 Self 都有类。在某种程度上,你可能会争辩说,区别只是面向原型的语言暴露了它们的实现。因此,最好不要把它变成一件大事:它可能更像是 EMACS 和 vi 之间的争论。
25赞 Adam Arold 11/6/2013
有用的答案。+1 评论中不太有用的垃圾。我的意思是,CLOS还是Smalltalk是第一个有区别吗?无论如何,这里的大多数人都不是历史学家。
24赞 Amit 5/3/2009 #2

你应该看看 Douglas Crockford 写的一本关于 JavaScript 的好书。它很好地解释了 JavaScript 创建者做出的一些设计决策。

JavaScript 的一个重要设计方面是它的原型继承系统。对象在 JavaScript 中是一等公民,以至于常规函数也被实现为对象(准确地说是“函数”对象)。在我看来,当它最初被设计为在浏览器中运行时,它旨在用于创建大量单例对象。在浏览器 DOM 中,您会发现该窗口、文档等都是单例对象。此外,JavaScript 是松散类型的动态语言(与强类型的 Python 相反,动态语言),因此,通过使用“原型”属性实现了对象扩展的概念。

所以我认为在 JavaScript 中实现的基于原型的 OO 有一些优点:

  1. 适用于松散类型的环境,无需定义显式类型。
  2. 使实现单例模式变得非常容易(在这方面比较 JavaScript 和 Java,你就会知道我在说什么)。
  3. 提供在不同对象的上下文中应用对象的方法、从对象动态添加和替换方法的方法等(这在强类型语言中是不可能的)。

以下是原型OO的一些缺点:

  1. 没有实现私有变量的简单方法。使用 Crockford 的魔法和闭包实现私有变量是可能的,但它绝对不像在 Java 或 C# 中使用私有变量那样简单。
  2. 我还不知道如何在 JavaScript 中实现多重继承(它的价值)。

评论

2赞 aehlke 12/13/2010
只需像 Python 一样对私有变量使用命名约定。
1赞 Benja 10/20/2012
在 JS 中,执行私有 var 的方法是使用闭包,这与您选择的继承类型无关。
7赞 Hal50000 10/20/2016
Crockford 对 JavaScript 做了很多事情,因为一种相当简单的脚本语言已经演变成对其内部结构的大师级迷恋。JS 没有真正的私有关键字范围或真正的多重继承:不要试图伪造它们。
44赞 Vijay Mathew 12/13/2010 #3

在《自我:简单的力量》一文中可以找到略微偏向于基于原型的方法的比较。本文提出了以下支持原型的论点:

Creation by copying. Creating new objects from prototypes is accomplished by a simple operation, copying, with a simple biological metaphor, cloning. Creating new objects from classes is accomplished by instantiation, which includes the interpretation of format information in a class. Instantiation is similar to building a house from a plan. Copying appeals to us as a simpler metaphor than instantiation.

Examples of preexisting modules. Prototypes are more concrete than classes because they are examples of objects rather than descriptions of format and initialization. These examples may help users to reuse modules by making them easier to understand. A prototype-based system allows the user to examine a typical representative rather than requiring him to make sense out of its description.

Support for one-of-a-kind objects. Self provides a framework that can easily include one-of-a-kind objects with their own behavior. Since each object has named slots, and slots can hold state or behavior, any object can have unique slots or behavior. Class-based systems are designed for situations where there are many objects with the same behavior. There is no linguistic support for an object to possess its own unique behavior, and it is awkward to create a class that is guaranteed to have only one instance [think singleton pattern]. Self suffers from neither of these disadvantages. Any object can be customized with its own behavior. A unique object can hold the unique behavior, and a separate "instance" is not needed.

Elimination of meta-regress. No object in a class-based system can be self-sufficient; another object (its class) is needed to express its structure and behavior. This leads to a conceptually infinite meta-regress: a is an instance of class , which is an instance of metaclass , which is an instance of metametaclass , ad infinitum. On the other hand, in prototype-based systems an object can include its own behavior; no other object is needed to breathe life into it. Prototypes eliminate meta-regress.pointPointPointPoint

Self is probably the first language to implement prototypes (it also pioneered other interesting technologies like JIT, which later made its way into the JVM), so reading the other Self papers should also be instructive.

评论

5赞 Max Nanasy 9/6/2012
RE: Elimination of meta-regress: In the Common Lisp Object System, which is class-based, a is an instance of class , which is an instance of metaclass , which is an instance of itself, ad finitum.pointPointstandard-class
1赞 user1201917 4/23/2020
Links to a self papers are dead. Working links: Self: The Power of Simplicity | A Self Bibliography
1赞 ezio 7/22/2022 #4

The difference between mainstream OOP class-based languages such as c# or java and prototype bases languages such as javascript is the ability to modify object types at runtime whilst in c# or java they gave up this ability in favor of static type checking by making classes fixed at compile time. JS has always been closer to the first original design of OOP of alan Kay and languages such as Smalltalk or simula.

this is achieved by making the blueprint itself an instance, types in prototype-based are actual instances that can be accessed and modified at runtime, in Javascript this is really easy using the prototype object, since every object type has this object.

example: type funcName.prototype.myNewMethod= function{ console.log("hello world")}