基于原型与基于类的inheritance

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

在Python,C ++等中,有一些类和实例是独立的概念。 为了做inheritance,你必须使用基类创build一个新的类,然后可以用它来产生派生实例。

为什么JavaScript会朝这个方向发展(基于原型的面向对象)? 基于原型的面向对象的传统的基于类的面向对象的优点(和缺点)是什么?

这里大概有一百个术语问题,主要是围绕某个人(而不是你)尝试使自己的想法听起来像“最好”一样。

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

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

现在,就比较而言:

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

后来OO语言希望能够使用静态types检查,所以我们在编译时得到了一个固定类的概念。 在开放式的版本中,你有更多的灵活性; 在较新的版本中,您有能力在编译器中检查某些types的正确性,否则这些正确性将需要testing。

在“基于类”的语言中,复制发生在编译时。 在原型语言中,操作存储在原型数据结构中,并在运行时进行复制和修改。 但是,抽象地说,一个类仍然是共享相同状态空间和方法的所有对象的等价类。 在原型中添加方法时,可以有效地创build新的等价类的元素。

那么,为什么呢? 主要是因为它在运行时使得一个简单,合乎逻辑,优雅的机制。 现在,要创build一个新的对象, 或者创build一个新的类,您只需执行深度复制,复制所有数据和原型数据结构。 您可以免费得到多态或多态的inheritance和多态:方法查找总是由一个字典来查询一个方法实现的名称。

以Javascript / ECMA脚本结尾的原因基本上是,当我们开始使用这个10年前的时候,我们正在处理function比较弱的计算机和不太复杂的浏览器。 select基于原型的方法意味着解释器可以非常简单,同时保持物体方向的期望属性。

一个比较,稍微偏向于基于原型的方法,可以在文章中find – 自我:简单的力量 。 本文提出以下赞成原型的论点:

通过复制创build 。 从原型创build新的对象是通过一个简单的操作,复制,用一个简单的生物隐喻克隆完成的。 从类创build新的对象是通过实例化来完成的,其中包括对类中格式信息的解释。 实例化与从计划中构build房子类似。 复制呼吁我们作为一个比实例化更简单的比喻。

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

支持独一无二的对象 。 Self提供了一个框架,可以很容易地将自己的行为包括在一个对象中。 由于每个对象都有命名的槽,槽可以保持状态或行为,任何对象都可以有唯一的槽或行为。 基于类的系统devise用于存在许多具有相同行为的对象的情况。 对于一个对象来说,没有语言上的支持来拥有自己独特的行为,而且它是尴尬的( 想想Singleton模式 )来创build一个保证只有一个实例的类。 自己也没有这些缺点。 任何对象都可以用自己的行为来定制。 一个独特的对象可以保持独特的行为,并且不需要单独的“实例”。

消除元回归 。 基于class级制度的任何对象都不能自给自足, 需要另一个对象(它的类)来expression它的结构和行为。 这导致了一个概念上无限的元回归:一个point是类Point一个实例,它是元类Point一个实例,它是metameta类Point一个实例,ad infinitum。 另一方面,在基于原型的系统中,对象可以包含自己的行为; 没有其他的东西需要呼吸。 原型消除荟萃回归。

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

你应该看看Douglas Crockford 编写的一本关于JavaScript的好书 。 它对JavaScript创build者所做的一些devise决定提供了非常好的解释。

JavaScript的重要devise方面之一是其原型inheritance系统。 对象是JavaScript中的第一类公民,非常规则的函数也被实现为对象(“函数”对象是精确的)。 在我看来,当它最初是为了在浏览器中运行而devise的时候,它被用来创build大量的单例对象。 在浏览器的DOM中,你可以find那个窗口,文档等所有的单例对象。 另外,JavaScript是松散types的dynamic语言(而不是强types的Python,dynamic语言),因此通过使用'prototype'属性来实现对象扩展的概念。

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

  1. 适合松散types的环境,不需要定义显式types。
  2. 使它非常容易实现单例模式(在这方面比较JavaScript和Java,你就知道我在说什么了)。
  3. 提供在不同对象的上下文中应用对象方法的方法,从对象中dynamic添加和replace方法等等(在强types语言中不可能的东西)。

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

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