写在前面, 本文中实现的运算符重载有很多局限, 完全不能实用, 仅供烧脑和娱乐.
很久以前做过比较完善的 函数重载实现 , 然后自从知道 valueOf
这个东西, 就一直有想搞运算符重载的念头. 搜了下居然搜到自己 11 年发的 帖子 , 对 valueOf
进行了简单的利用.
前些时候在公司做分享, 主题是 WAT JavaScript Explained, 最后是以 JavaScript 尝试重载向量的 +-*/
运算符结束的, 下文则是在分享中设计的效果与方法.
Vector.createVector('a', 1, 2); Vector.createVector('b', 4, 6); Vector.createVector('c'); c = a + b; c = a - b; c = a * 2; c = b / 4;
Vector.createVector('a', 1, 2); Vector.createVector('b', 4, 6); Vector.createVector('c', 2, 1); Vector.createVector('d'); d = a + b - c; d = -a - b + c;
文中的技巧还可以实现类似的效果:
let a = new Vector(1, 2); let b = new Vector(4, 6); let c = new Vector(4, 6); let d = calc(a + b - c);
细心的同学可能已经发现, 上面提到的例子中, 要么使用了全局变量, 要么需要包裹一个函数 calc
. 通过重写 valueOf
方法, 我们可以知道哪些向量参与了运算. 但仅仅如此, 还无法知道参与运算的运算符, 也无法知道第一种实现中类似于 a * 2
和 b / 4
中的 2
和 4
. 但如果我们可以获得整个表达式的值, 就可以从某种程度窥探发生了怎样的运算.
+
和 -
前面我们提到了重写 valueOf
方法获取参与了运算的向量, 但为了知道发生了怎样的运算, 我们还需要为 valueOf
构造特别的返回值. 比如第一个参与运算的向量 a
对应的值为 1
, 第二个向量 b
对应的值为 2
, 那么 a + b
的值则为 3
, a - b
则为 -1
. 这样一来, 通过 setter 或者 calc
函数我们就可以根据表达式的值得知发生了什么运算.
虽然只提到了 +
和 -
, 同样的方法我们还可以实现向量的叉乘.
*
和 /
对于四则运算中的原始值, JavaScript 并不会调用对应的 valueOf
. 这样一来, 我们则需要争取通过获得的表达式的值获取运算符和标量. 或者, 或许我们并不需要知道到底是哪一个运算符, 只需要知道这个向量需要缩放的比例. 上面我们提到了第一个参与运算的向量 valueOf
的值是 1
, 这自然而然地带来了一个好处: 表达式 a * n
( a
为向量, n
为标量) 的值是 n
, b / m
( b
为向量, m
为标量) 的值则是 1 / m
. 向量需要缩放的比例正好是表达式最后的值! 现在知道了参与运算的向量和它需要被缩放的比例, 也就自然可以计算出结果.
+
和 -
上面提到的方式中, 构造 valueOf
的值相对简单, 只要不为零都是可以的. 然而很显然, 如果向量数量大于 2, 这种构造的方式得出的表达式的值很难再推出发生了哪些运算. 不过仅仅是针对 +
和 -
两种运算, 我们还是有办法区分一定数量的向量参的运算.
思考表达式 a + b - c + d - e
, 怎样可以使它的值包含所有的运算符信息? 可能有的同学也会立即把它和 flags 或者 enums 之类的联系到一起:
let flagA = 0b0001; let flagB = 0b0010; let flagAB = flagA | flagB; // 0b0011
如果我们可以把类似的性质应用到加减上, 就可以保留一定数量的运算符信息了. 考虑三进制数的加减: 1111 + 1 - 10 + 100 - 1000 = 0202. 和表达式 a + b - c + d - e
联系起来, 0202 中的四个数字分别对应了 -
( e
), +
( d
), -
( c
) 和 +
( b
).
这样一来, 我们为第一个参与运算的向量构建足够大的每一位都为 1 的三进制数作为 valueOf
的返回值, 为随后参与运算的向量分别构建 1, 10, 100 这样的三进制数, 就可以通过表达式的值获知几十个参与运算的向量对应的符号了. 对于首个元素前面有负号的情况, 则可以通过整个表达式的正负来做区分.
上面提到的两种效果的实现都可以在这个仓库找到: vilic/js-operator-overloading .
扫码关注w3ctech微信公众号