【发布时间】:2011-11-09 19:00:15
【问题描述】:
为什么我们要构建原型继承链而不是使用对象组合。为链中的每个步骤查找原型变得很昂贵。
这是一些虚拟示例代码:
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.foo 比 o1.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