基于类的继承和基于原型继承的区别
JavaScript 的继承是基于原型(prototype)实现的。它的创造者 Brendan Eich 仅仅花了十天便创造了 JavaScript。Brendan Eich 对这个新语言的设计原则是功能不不需要太强,语法越简单越好。于是便借鉴 Self 语⾔实现了 JS 的继承模型。至于 Brendan Eich 为什么没有借鉴当时火的一塌糊涂的 JAVA 的继承思想,这还要从面向对象继承和原型继承的区别说起。
传统的基于类的面向对象语言,基于根深蒂固的二元性:
- 类定义对象的基本性质和行为。
- 对象实例是类的特定体现(manifestation)。
例如,假设 Vehicle
(车辆)类的对象有一个名称,并且能够执行各种操作,比如“开车上班”和“运送建材”。Bob's car
是类 Vehicle
的特定对象(实例),它的“名字”是“Bob’s car”。在理论上,你可以向 Bob's car
发送消息,告诉它去“运送建材”。
这个例子展示了这种方式的一个问题:如果 Bob's car
恰巧是一辆运动车,那么它将不能运送建材,但这是类 Vehicle
所必须提供的功能。通过使用子类来创造专业的 Vehicle
可产生一种更符合实际的模型;例如 Sports Car
(运动车)和 Flatbed Truck
(平板卡车)。只有类 Flatbed Truck
的对象需要提供“运送建材”的功能;运动车只需要提供“快速行驶”的功能。然而,这种更深层次的模型需要在设计期间更有洞察力,但是这一般很难做到。
除非设计者能确定地预知一组对象和类在未来都有哪些基本性质和行为,否则将不能设计好一个类的层级。程序最终需要增加行为实在是太频繁了,而系统的很多节段将需要重新设计(或重构)来以适应不同的新场景。系统往往会成长到一定程度然后变得非常僵化(变成屎山)。因为在设计者在最原始时候设计的基类,将逐渐成为一个简单的“错误”。
在基于原型的编程语言中,例如 JavaScript
消除了在类和对象之间的这种二元性。
不再有基于某种“类”的一个对象“实例”,在 JavaScript 中,可以复制一个现有的对象,并改变它。所以 Bob's car
可以通过制作现有的 Vehicle
对象的副本来创建,并增加“快速行驶”方法。用来制作副本的基本对象叫做“原型”。
“原型” 的实现也有两种模式 —— “委托原型” 和 “串接原型”。
JavaScript
是“委托原型”,即在本对象上没有的属性会一直沿着原型链向上寻找。
而“串接原型”类似于生物学上的分裂,属性和方法被原样复制。这样做的好处是对象的作者可以修改这份副本而无须担心对此父类的其他子类产生副作用,但是坏处是会浪费很多内存。
参考链接: