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

在Python, c++等。类和实例是分开的概念。为了进行继承,您必须使用基类来创建一个新类,然后可以使用它来生成派生实例。

为什么JavaScript会走向这个方向(基于原型的面向对象)?与传统的基于类的面向对象相比,基于原型的面向对象的优点(和缺点)是什么?


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

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

数据的封装以及对数据的相关操作,这些操作可以称为数据成员和成员函数,也可以称为数据和方法。 继承,即说这些对象除了这些变化之外与其他对象集一样的能力 多态(“多种形状”),其中对象自行决定要运行哪些方法,以便您可以依赖语言正确地路由请求。

现在,就比较而言:

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

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

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

为什么这么做呢?主要是因为它在运行时形成了一种简单、合理、优雅的机制。现在,要创建一个新对象,或者创建一个新类,您只需执行一个深度复制,复制所有数据和原型数据结构。这时你或多或少可以免费获得继承和多态性:方法查找总是由按名称向字典查询方法实现组成。

最终使用Javascript/ECMA脚本的原因基本上是,当我们10年前开始使用这个脚本时,我们要处理的计算机功能要弱得多,浏览器也不复杂得多。选择基于原型的方法意味着解释器可以非常简单,同时保留面向对象的理想属性。


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

One of the important design aspects of JavaScript is its prototypal inheritance system. Objects are first class citizens in JavaScript, so much that regular functions are also implemented as objects ('Function' object to be precise). In my opinion, when it was originally designed to run inside a browser, it was meant to be used to create lots of singleton objects. In browser DOM, you find that window, document etc all singleton objects. Also, JavaScript is loosely typed dynamic language (as opposed to say Python which is strongly typed, dynamic language), as a result, a concept of object extension was implemented through the use of 'prototype' property.

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

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

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

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


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

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 point is an instance of class Point, which is an instance of metaclass Point, which is an instance of metametaclass Point, 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.

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


主流面向对象的基于类的语言(如c#或java)和原型语言(如javascript)之间的区别是能够在运行时修改对象类型,而在c#或java中,他们放弃了这种能力,通过在编译时固定类来支持静态类型检查。JS一直更接近alan Kay和Smalltalk或simula等语言的第一个面向对象的原始设计。

这是通过使蓝图本身成为一个实例来实现的,基于原型的类型是可以在运行时访问和修改的实际实例,在Javascript中使用原型对象非常容易,因为每个对象类型都有这个对象。

示例:类型funcName.prototype。myNewMethod= function{console.log("hello world")}