【发布时间】:2013-11-28 13:14:19
【问题描述】:
我想以一种可以交换基础数字类型的方式来制定代数表达式。如果你愿意,可以考虑复数、大整数、矩阵等。出于这个原因,我会写add(a, b) 或a.add(b) 而不是a + b。在静态类型语言中,我只需使用函数add 的基于类型的重载来实现各种替代方案。但是对于 JavaScript,这不起作用,所以我正在寻找替代方案。执行的方法取决于 both 操作数的类型。
我想出的一种方法是以下double dispatch 机制:
将表达式写为
a.add(b)。-
通过以下方式为给定类型(例如我自己的
Complex类型或内置的Number类型)实现该方法:add: function(that) { that.addComplex(this); }所以第二次调用的方法名编码了其中一个操作数的类型。
-
实施专门的方法来处理所有组合。例如,设置
Number.prototype.addComplex = function(that) { return newComplex(that.real + this, that.imaginary); }
假设我知道所有类型,因此我可以确保处理所有组合。我现在困扰的更多的是这些对象的创建。
上述方法严重依赖于虚拟方法分派,所以我认为它需要某种继承。经典的构造函数没有问题,但根据我刚刚所做的this jsperf,使用构造函数创建对象往往比对象文字慢。有时速度会慢很多,例如本例中的 Firefox。所以我不愿意为 every 承担这种开销,例如复数值中间值只是为了让我的运算符重载工作。
我在这个 jsperf 中尝试的另一种方法不是使用原型,而是将虚拟方法存储为每个单个对象实例的属性。在几乎所有经过测试的浏览器上都运行得非常快,但在这里我担心对象的大小。我担心对象有两个实际的浮点值,但可能有多达 50 个不同的成员函数来处理所有成对的运算符重载。
第三种方法是使用单个 add 函数,它以某种方式检查其参数的类型,然后根据它做出决定。可能在一些由一些数字类型标识符的组合索引的列表中查找实际实现。我还没有写出来测试,但是这种类型检查感觉很慢,而且我也怀疑 JIT 编译器是否能够优化这种奇特的函数调度。
有没有什么方法可以诱使当前的 JavaScript 实现对创建成本低且不占用过多内存的对象进行适当优化的双重分派?
【问题讨论】:
-
您的“Object.create”测试需要一个
var用于局部变量“pt”。如果您在闭包中使用本地函数的“对象文字”上做一个变体,它甚至比那个更快(无论如何在一次测试运行中)。 jsperf.com/object-creation-mvg/2 -
我在这个 jsper 上看不到,“经典构造函数”工作缓慢,而且,您似乎需要将除“runtest(ctorPoint);”之外的所有内容都删除到设置部分,所以你不会测量“类”初始化...
标签: javascript performance oop inheritance double-dispatch