【问题标题】:Why use chained prototype inheritance in javascript?为什么在 javascript 中使用链式原型继承?
【发布时间】:2011-11-09 19:00:15
【问题描述】:

perf

为什么我们要构建原型继承链而不是使用对象组合。为链中的每个步骤查找原型变得很昂贵。

这是一些虚拟示例代码:

var lower  = {
    "foo": "bar"
};

var upper = {
    "bar": "foo"
};

var chained = Object.create(lower,  pd(upper));

var chainedPrototype = Object.create(chained);

var combinedPrototype = Object.create(pd.merge(lower, upper));

var o1 = Object.create(chainedPrototypes);
var o2 = Object.create(combinedPrototypes);

使用pd,因为属性描述符非常冗长。

o2.fooo1.foo 更快,因为它只上升了两个原型链而不是三个。

既然沿着原型链向上移动很昂贵,为什么我们要构造一个而不是使用对象组合?

另一个更好的例子是:

var Element = {
  // Element methods
}

var Node = {
  // Node methods
}

var setUpChain = Object.create(Element, pd(Node));
var chained = Object.create(setUpChain);
var combined = Object.create(pd.merge(Node, Element));

document.createChainedElement = function() {
  return Object.create(chained);
}

document.createCombinedElement = function() {
  return Object.create(combined);
}

我没有看到任何代码合并原型对象以提高效率。我看到很多代码构建链式原型。为什么后者更受欢迎?

我能想到的唯一原因是使用Object.isPrototypeOf 来测试您链中的各个原型。

除了isPrototypeOf,使用继承优于组合还有哪些明显的优势?

【问题讨论】:

  • 请注意,每个自尊 JIT 编译器的优化之一(不仅在 JS 中,例如 PyPy 对 Python 对象执行此操作 - 而 Python 的对象属性查找更加复杂!)寻找常见的对象布局和查找链并优化访问以直接计算偏移量。请参阅morepypy.blogspot.com/2010/11/…,它解释了一般思想(在基于类的设置中)和其他 JIT 编译器(例如 V8)中类似优化的资源链接。您的基准确认 o1 和 o2 之间几乎没有区别。
  • @delnan 你认为担心原型链上的额外计算成本是我们不应该关心的微优化吗?
  • 这甚至更多 - 这是使用脑死亡解释器时的微优化。当使用一个好的 JIT 编译器(并且这些编译器每年都变得越来越普遍甚至更好)时,完全有可能根本不会有任何区别(当然是在 JIT 预热之后)。
  • @Raynos,我确实认为这是微优化,因为使用原型(内存利用率和 instanceof)有明显的好处。如果您发现某些对象的查找是瓶颈,那么您应该继续优化该部分。

标签: javascript oop prototypal-inheritance prototype-programming


【解决方案1】:

主要原因必须是原型对象的更改。对祖先对象的更改将反映在整个链中。可以想象,这可能是一个好处。虽然我无法立即想到任何现实世界的实例,但我认为拥抱这种动态特性可以提供其他(阅读:基于类)语言根本不提供的动态。

原型链上游的对象可以在应用程序的整个生命周期内根据需要发展,这些变化将反映在所有后代对象中。这可以很容易地与作为第一类对象的 JavaScript 函数结合,以根据需要动态修改功能。

也就是说,如果不需要此功能,则没有理由使用原型链而不是组合。

【讨论】:

  • 谢谢,我忘了对象不是通过引用传递的。
  • @Raynos 它们是通过引用传递的,但是你是在复制对象属性,而不是通过引用的原型查找它?
【解决方案2】:

好吧,考虑一下如果 lowerupper 发生变化会发生什么。组合的原型不会反映这种变化,因为您通过复制新对象的属性创建了一个新对象。

在许多情况下都可以,但肯定不如为您的对象实际构建适当的原型链那样动态。

【讨论】:

    【解决方案3】:

    以下是我能想到的一些好处,按重要性排序

    内存使用

    通过使用原型,您可以创建共享属性。您的方法将所有值复制到每个对象。

    设置对象的前期成本

    认为您稍后会节省一点时间,但您在设置对象时会产生复制属性的成本。您最好在性能测试中考虑到这一点。如果您阅读的内容远多于设置对象的内容,那么这一优势可能会被抵消。

    instanceOf

    好的代码不用instanceOf,但有时你不能让你的所有代码都完美,那么为什么要破坏一个语言特性呢?

    动态更改原型

    大多数人会声称他们从不需要这个(像我一样),但是我们中的许多人在实例化一些数组之后扩展了 Array.prototype(不是你应该这样做)。使用复制属性方法,您将失去对原始对象的引用。

    无耻的插头:http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

    最后一点如果您这实际上是应用程序中的瓶颈,我不会不愿意将它用于有问题的对象

    【讨论】:

    • 内存使用差别很小,并且是特定于实现的。
    • isPrototypeOf 仍然可以在组合情况下使用。除了每个对象只能在类型上。
    猜你喜欢
    • 2023-03-25
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 1970-01-01
    • 2015-10-29
    • 1970-01-01
    • 2010-09-28
    相关资源
    最近更新 更多