经典与原型继承

IT技术 javascript oop
2021-03-03 21:52:57

在阅读了两者之后,我只是好奇,编程社区如何使用它?在什么情况下哪个?

3个回答

经典继承有很多原型继承不存在的问题,例如:

经典传承

紧耦合继承是面向对象设计中可用的最紧密的耦合。后代类对其祖先类有深入的了解。

不灵活的层次结构(也就是必要的重复)单父层次结构很少能够描述所有可能的用例。最终,所有层次结构对于新用途都是“错误的”——这个问题需要重复代码。

多重继承很复杂从多个父母那里继承通常是可取的。该过程异常复杂,其实现与单继承的过程不一致,这使得阅读和理解变得更加困难。

脆弱的架构由于紧密耦合,通常很难重构具有“错误”设计的类,因为许多现有功能依赖于现有设计。

大猩猩/香蕉问题通常,您不想继承父代的某些部分。子类化允许您覆盖父级的属性,但不允许您选择要继承的属性。

原型继承

要了解原型继承如何解决这些问题,您首先应该了解原型继承有两种不同的类型。JavaScript 同时支持:

委托如果在实例上找不到属性,则会在实例的原型上搜索它。这使您可以在许多实例之间共享方法,免费为您提供享元模式

串联向对象动态添加属性的能力使您能够将任何属性从一个对象自由复制到另一个对象,一起或有选择地复制。

您可以结合两种形式的原型继承来实现非常灵活的代码重用系统。事实上,如此灵活,以至于用原型实现经典继承是微不足道的。反过来就不对了。

原型继承允许您在经典语言中找到的大多数重要功能。在 JavaScript 中,闭包和工厂函数允许您实现私有状态,并且函数继承可以轻松地与原型结合,以便添加支持数据隐私的 mixin。

原型继承的一些优点:

松耦合实例永远不需要直接引用父类或原型。可以存储对对象原型的引用,但不建议这样做,因为这会促进对象层次结构中的紧密耦合——经典继承的最大缺陷之一。

扁平的层次结构使用原型 OO 保持继承层次结构平坦是微不足道的 - 使用串联和委托,您可以拥有单一级别的对象委托和单个实例,而无需对父类的引用。

琐碎的多重继承从多个祖先继承就像使用串联组合来自多个原型的属性一样简单,以形成一个新对象,或一个新对象的新委托。

灵活的架构由于您可以使用原型 OO 有选择地继承,因此您不必担心“错误设计”的问题。新类可以从源对象的任何组合继承任何属性组合。由于层次结构扁平化很容易,一个地方的变化不一定会在整个后代对象的长链中引起涟漪。

没有大猩猩了选择性继承消除了大猩猩香蕉问题。

我不知道经典继承比原型继承有什么优势。如果有人知道,请赐教。

我是专门谈论 JavaScript(由 OP 标记),它没有静态类型。JavaScript 中的类与 Java 中的静态类完全不同。instanceof是一种不可靠的检查,当对象扩展或跨越执行上下文边界时不起作用。即使在 TypeScript 中,接口也是一种更可靠的开发工具机制。
2021-04-22 21:52:57
@Lakshay 使用串联继承。此处涵盖:关于 JavaScript 中继承的常见误解
2021-05-02 21:52:57
好的答案虽然有点偏颇,但您的最后一条评论证明您没有对经典优势进行过多思考。我是一名 JS 开发人员,但我可以看到静态分析的好处,IDE 的更强大的重构能力,即时文档以及总结一些更快速的方法来深入研究一些外国代码,特别是如果它的所有者不相信和你一样的原则。
2021-05-05 21:52:57
经典继承的优势不是比原型继承更快,尤其是例如在 C++ 中使用编译时静态类?或者你的意思是严格限于只涉及 JavaScript 的讨论?
2021-05-06 21:52:57
如何在javascript中有选择地继承?
2021-05-20 21:52:57

基于原型的继承更加灵活。任何现有的对象都可以成为一个类,从中可以产生其他对象。当您的对象提供多组服务和/或在您的程序到达需要继承的点之前它们经历了大量状态转换时,这很方便。

建模方法的广泛讨论可在此处获得:http : //steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

由于 Javascript 不支持大多数人理解的“经典”继承(并且您没有对您正在阅读的内容提供任何参考),我假设您的意思是继承处理如下:-

 function base() {
    var myVar;
    this.someBaseFunc = function() { }
 }


 function derived() {
    base.call(this);
    var someOtherVar;
    this.SomeOtherFunc = function() { }
 }

我的一般经验法则是:

  • 如果类很复杂并且实例很少,则使用“经典”方法。这允许类仅公开那些应该公开的功能。
  • 如果类很简单并且实例很多,则使用原型方法。这限制了在创建实例时反复定义和存储对函数的引用的开销。
哦,是的,闭包(部分计算的函数)或利用周围范围内的变量的函数可以像具有多态性和封装性的对象一样使用,它比对象更轻量级,尽管您仅限于调用函数,而对象通常有几种方法。
2021-04-21 21:52:57
函数内定义的所有方法都可以通过闭包访问该函数的私有数据。该数据被封装。const createSecret = secret => ({ getSecret: () => secret, setSecret: newSecret => createSecret(newSecret) })
2021-04-22 21:52:57
我讨厌贬低事物,但您的答案都不正确。抱歉。); * 你不需要经典继承来实现封装。您所需要的只是一个闭包(可以使用工厂函数和原型轻松完成)。* JavaScript 中的经典继承仍然可以利用原型委托(并且经常这样做)。经典继承只是意味着新实例从父子层次结构中的父类继承,而不是原型中的对象实例。
2021-05-05 21:52:57