在JavaScript中,每个对象同时是一个实例和一个类。要进行继承,可以将任何对象实例用作原型。
在Python,C++等。有类和实例作为单独的概念。为了进行继承,必须使用基类创建一个新的类,然后才能使用该类生成派生实例。
为什么JavaScript会朝这个方向发展(基于原型的对象方向)?与传统的基于类的OO相比,基于原型的OO有哪些优点(和缺点)?
- Javascript受Self的影响,Self是第一种具有原型继承的语言。当时,古典继承风靡一时,最早出现在Simula中。然而,传统继承过于复杂。然后,David Ungar和Randall Smith在阅读了GEB之后有了顿悟:"最具体的事件可以作为事件类的一般例子。"他们意识到,面向对象编程不需要类。因此,自我诞生了。要了解原型继承如何优于经典继承,请阅读:stackoverflow.com/a/16872315/783743=)
- 感谢你提出了一个有趣的问题,并以一种对我来说已经可以作为答案的方式介绍它。)
- @Aadimshah什么/谁是GEB?
- @亚历克斯·盖布是道格拉斯·霍夫施塔特写的一本书。它是G&246;del escher bach的缩写。库尔特·格德尔是一位数学家。埃舍尔是个艺术家。巴赫是钢琴家。
这里有大约一百个术语问题,主要围绕着某人(而不是你),试图让他们的想法听起来像是最好的。
所有面向对象的语言都需要能够处理几个概念:
对数据的封装以及对数据的相关操作,这些操作有各种各样的名称,如数据成员和成员函数,或者作为数据和方法等等。
继承,表示这些对象与其他对象集一样的能力,除了这些更改
多态性("许多形状"),其中一个对象本身决定运行什么方法,以便您可以依赖语言来正确地路由请求。
现在,就比较而言:
首先是整个"类"和"原型"问题。这个想法最初是从Simulat开始的,在Simulat中,使用基于类的方法,每个类表示一组共享相同状态空间(读取"可能值")和相同操作的对象,从而形成一个等价类。如果回顾smalltalk,因为您可以打开一个类并添加方法,这实际上与您在javascript中所做的相同。
后来OO语言希望能够使用静态类型检查,所以我们在编译时得到了固定类集的概念。在开放类版本中,您有更多的灵活性;在较新的版本中,您有能力在编译器中检查某些类型的正确性,否则将需要进行测试。
在"基于类的"语言中,复制是在编译时进行的。在原型语言中,操作存储在原型数据结构中,该结构在运行时进行复制和修改。然而,抽象地说,类仍然是共享相同状态空间和方法的所有对象的等价类。当您向原型添加一个方法时,实际上是在生成一个新等价类的元素。
现在,为什么要这样做?主要是因为它在运行时提供了一个简单、逻辑、优雅的机制。现在,要创建一个新对象,或者创建一个新类,只需执行一个深度复制,复制所有数据和原型数据结构。您可以免费获得继承和多态性:方法查找通常包括按名称向字典请求方法实现。
最终出现在javascript/ecma脚本中的原因基本上是,当我们10年前开始使用这个脚本时,我们正在处理的是功能弱得多的计算机和复杂得多的浏览器。选择基于原型的方法意味着解释器可以非常简单,同时保持对象方向的理想属性。
- 实际上,Simula在Smalltalk之前,进行了静态类型检查和继承。实际上,Simula是"class"这个词的发源地。
- 对,那个段落读起来好像我不是有意的吗?dahl和nyqvist提出了"类"作为具有相同方法签名的集合。
- 这种变化会说得更好吗?
- 我想说,javascript完全代表CLOS对象模型,甚至不是smalltalk或simula。早在Smalltalk IIRC之前,Clos就在附近。
- 不,抱歉,CLOS来自80年代末的DreamStongs.com/clos.html Smalltalk,来自1980年的en.wikipedia.org/wiki/Smalltalk和Simula,1967-68年的en.wikipedia.org/wiki/Simula提供了完整的对象定向。
- 如果能找到关于"味道"的论文——CLOS的前身,你会发现CLOS大约出现在70年代后期:coding.derkeiler.com/archive/lisp/comp.lang.lisp/2004-09/…
- 在1967年之后。《人工智能备忘录602》首次报道了口味,该备忘录表面上说,它提供了与Smalltalk和Actor相同的功能,从而证明了它与Smalltalk的当代性;当然,它不会比Smalltalk早。
- 出版物.ai.mit.edu/ai-publications/pdf/aim-602.pdf
- 只是仔细阅读,我只讲了些小故事。
- 还有另一个预CLOS系统-公共循环。我不太确定它是不是也是Smalltalk的掠食者?然后这两个部分(风味和Cloops)被分成Clos。
- 以及你1967年的参考资料,我将标记为垃圾邮件。很抱歉。
- @查理:谢谢,对我很有用。
- 因此,据我所知,基于类继承的思想更适合于静态类型语言。但是为什么python选择这个策略而不是基于原型的策略呢?
- @Stephano,它们并没有那么明显:python、ruby、smalltalk使用字典查找方法,javascript和self也有类。在某种程度上,您可能会认为区别在于面向原型的语言正在公开它们的实现。所以最好不要把它变成大问题:它可能更像Emacs和vi之间的争论。
- 我记得Joel&Jeff在一个so播客中讨论了javascript的开发。iirc javascript被设想为一种功能性语言,但最后一分钟的管理恐慌导致了OOP的添加。
- 是的,那大概是"面向对象"被用作"好"的同义词的时候。尽管如此,与self的直接关系,特别是所谓的面向对象关系,表明说JS是OO是正确的。
- 在Smalltalk中,可以向类添加方法,但不能更改对象的类。(但是,您可以将一个对象替换为另一个对象,这是普遍的。)
- 有用的答案。+1评论中有用的垃圾。我的意思是,CLOS还是Smalltalk是第一个呢?无论如何,这里的大多数人都不是历史学家。
- 很好的回答。只是一个问题:我们能把原型对象看作是灵活的、动态的类对象吗?
- 嗯,这是个有趣的想法。是啊。或者一个类,作为所有具有共享公共方法的原型的对象的等价类。哦,谢谢!
- 很好的回答。你的第一句话"这里有大约一百个术语问题,主要是围绕某人(而不是你)努力使他们的想法听起来像是最好的。"引起了我的注意。这正是我很久以前的想法,但我只是认为可能我错了,因为有这么多关于这个的帖子。我们所能说的是,每种类型都有其优缺点,没有一种比其他类型更好。
一个稍微偏向于基于原型的方法的比较,可以在本文中找到——自我:简单的力量。本文提出了以下有利于原型的论点:
通过复制创建。从原型创建新对象是由一个简单的操作,复制,用一个简单的生物学比喻,克隆。创建类中的新对象通过实例化完成,其中包括一个类中格式信息的解释。实例化是类似于从平面图上建造房屋。抄袭对我们来说是一个简单的比喻而不是实例化。
先前存在的模块示例。原型比类更具体因为它们是对象的例子,而不是格式和初始化的描述。这些示例可以帮助用户更容易地重用模块。理解。基于原型的系统允许用户检查典型代表而不是要求他从描述中理解。
支持独一无二的对象。Self提供了一个框架,可以轻松地将一种类型的对象包括在它们自己的行为中。因为每个对象命名槽,槽可以保持状态或行为,任何对象都可以有唯一的槽或行为。基于类的系统设计用于有许多对象具有相同的行为。没有语言支持对象拥有自己独特的行为,而创建一个保证只有一个实例的类是很尴尬的(想想单例模式)。自我不受这些不利因素的影响。任何对象都可以用自己的行为进行定制。唯一的对象可以保持唯一的行为,不需要单独的"实例"。
消除元回归。在基于类的系统中,没有对象是自给自足的;需要另一个对象(它的类)来表示它的结构和行为。这导致概念上无限的元回归:point是类的一个实例。point是元类point的一个实例,是元类的一个实例。point,无限大。另一方面,在基于原型的系统中,对象可以包括它自己的行为;不需要其他物体来向它呼吸生命。原型消除元回归。
Self可能是实现原型的第一种语言。(它还开创了其他有趣的技术,如JIT,后来它进入了JVM。所以阅读其他的自我论文也应该是有指导意义的)。
- 关于:消除元回归:在通用的基于类的Lisp对象系统中,point是类point的一个实例,是元类standard-class的一个实例,是它本身的一个实例,ad finitum。
你应该看看道格拉斯·克罗克福德的一本关于javascript的好书。它对JavaScript创建者所做的一些设计决策提供了非常好的解释。
Javascript的一个重要设计方面是它的原型继承系统。对象是JavaScript中的头等公民,所以常规函数也作为对象实现("函数"对象更精确)。在我看来,当它最初设计为在浏览器中运行时,它是用来创建许多单例对象的。在浏览器DOM中,您可以找到该窗口、文档等所有单例对象。另外,JavaScript是松散类型的动态语言(与强类型的动态语言python相反),因此,通过使用"原型"属性实现了对象扩展的概念。
因此,我认为在JavaScript中实现的基于原型的OO有一些优点:
适用于松散类型的环境,无需定义显式类型。
实现简单的模式非常简单(在这方面比较JavaScript和Java,你就会知道我在说什么)。
提供在不同对象的上下文中应用对象方法、从对象动态添加和替换方法等(强类型语言中不可能实现的内容)的方法。
以下是原型OO的一些缺点:
实现私有变量并非易事。使用CROROFROD的巫术使用闭包是可能实现私有VARS的,但是它绝对不象使用Java或C语言中的私有变量那么简单。
我还不知道如何在JavaScript中实现多个继承(为了它的价值)。
- 只需对私有var使用命名约定,就像python那样。
- 在JS中,私有变量的方法是使用闭包,这与您选择的继承类型无关。
- Crockford已经做了很多破坏javascript的工作,因为一个相当简单的脚本语言已经被变形为对其内部的精通的着迷。JS没有真正的私有关键字作用域或真正的多重继承:不要试图伪造它们。