串联继承和原型继承(在 JS 中,有时使用class
关键字)是两种不同的委托方法。委托是一种机制,对象可以通过该机制从其他对象而不是从类(在 Java 意义上)定义获取其部分或全部状态和行为。
在 JS 中,“继承”这个词与通过原型继承委托给祖先对象有很强的关联,但是(令人惊讶地)根据一些人的说法,这并非总是如此.
串联继承是将一个或多个源对象的属性组合成新的目标对象的过程。不管你相信与否,它是 JavaScript 中最常用的继承形式。
Also:
在 JS 中,连接继承的本质常常被通用名称“mixins”所掩盖。令人困惑的是,“mixins”在其他语言中甚至在某些 JavaScript 库中也有其他含义。它还有一些令人困惑的用途,称为“mixins”。由于这些原因,我更喜欢更精确的术语“串联继承”。
请注意,尽管 JavaScript 包含“类语法”(例如class foo extends bar {}
),它没有通常意义上的“经典”或基于类的继承。在 JavaScript 中,使用类语法的继承是always通过原型继承实现。因此,在 JavaScript 中,基于类的继承几乎完全是语法糖。原始原型继承模型自从 Brendan Eich 推出该语言的第一个十天版本以来,这一点就一直存在于 JavaScript 中。
“组合”是一个重载的术语。对象组合通常涉及让一个对象将功能或状态委托给其中包含的另一个对象。从技术上讲,它具体意味着子对象的生命周期与复合体的生命周期相关,但是,根据我的经验,“组合”通常用于表示“聚合”,即组合,但子对象的生命周期是不受复合体控制,它们通常通过构造函数注入。另一方面,函数组合是一种组合编程元素的模式其中函数调用嵌套在形式中f(g(x))
.
您可以通过遵循对象组合模式来实现串联继承,将对象提供给函数以串联到复合对象上。
在以下示例中,实例p
将包括提供的对象上存在的功能ancestor
.
function createP(ancestor) {
const p = {
...ancestor,
bar() {}
}
return p
}
const o = { foo() {} }
const p = createP(o) // `p` now has both methods `foo` and `bar`
对于 JS 中的原型继承,有一个固定的、语言支持的机制,用于通过原型链动态查找功能。
在原型继承中,继承的功能位于原型链上某个单独的对象上。这种间接性赋予了这种继承风格不同的非功能特征。
例如:
- 通过原型继承包含哪些功能可能比通过串联继承更不清楚,因为功能可以存在于(可能很长)原型链上的任何位置。此外,在创建对象后,可以在原型链中添加和删除功能。
- 对于 API,原型继承对于保留子对象的功能非常有用,使它们看起来简单,同时使它们能够以面向对象的方式方便地公开位于原型链上的功能。例如。你可以简单地打电话
Array#splice
直接在您创建的每个数组上:[].splice(...)
,尽管splice
函数位于其他地方(在Array.prototype
).
- 在原型继承中,祖先对象的方法需要以这样的方式编写:它们的目标(即
this
) 可能是另一个对象。可以说在串联继承中使用this
被弱化了。
- 在原型继承中,继承者和祖先之间存在持续的隐式耦合。这可能是错误的来源,并使代码更难以推理。
- 更一般地说,在串联继承中,内置的 JS 原型继承机制(包括
new
, this
, extends
, super
、原型链等)不再被强调。有一条思路,由道格拉斯·克罗克福德 (Douglas Crockford) 推广,这部分是JavaScript语言复杂化往往多于简单化,并且应该尽可能避免。串联继承提供了一种替代的继承机制。
- 串联继承绕过了原型继承的一些本体论约束。原型链接暗示着“is-a”关系,由此在串联继承中不暗示本体论关系。您可以将任意数量的对象混合在一起,以获得所需的功能(仅此而已)。
在两种方法之间进行选择取决于主观偏好和风格。两种方法都有其用处。