我已经使用JavaScript几天了,并且已经到了想要重载已定义对象的运算符的地步。
在谷歌搜索了一段时间后,似乎你不能正式这样做,但仍有一些人声称以某种冗长的方式执行此操作。
基本上,我已经创建了一个Vector2类,并希望能够执行以下操作:
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x += y; //This does not result in x being a vector with 20,20 as its x & y values.
相反,我不得不这样做:
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x = x.add(y); //This results in x being a vector with 20,20 as its x & y values.
有没有一种方法可以在我的Vector2类中重载运算符?因为这看起来很丑陋。
正如您所发现的,JavaScript不支持运算符重载。您可以实现的最接近的方法是实现toString
(当实例需要被强制转换为字符串时,它将被调用)和value eOf
(它将被调用以将其强制转换为数字,例如当使用进行加法时,或者在许多情况下使用它进行连接时,因为
尝试在连接之前进行加法),这是非常有限的。因此,两者都不允许您创建
Vector2
对象。同样,Proxy
(在ES2015中添加)允许您拦截各种对象操作(包括属性访问),但同样不允许您控制Vector
实例上=
的结果。
但是,对于想要字符串或数字作为结果(而不是Vector2
)的人来说,这里有value eOf
和toString
的示例。这些示例没有演示运算符重载,只是利用了JavaScript的内置处理转换为原语:
此示例将对象的val
属性的值翻倍,以响应被强制转换为原语,例如通过:
function Thing(val) {
this.val = val;
}
Thing.prototype.valueOf = function() {
// Here I'm just doubling it; you'd actually do your longAdd thing
return this.val * 2;
};
var a = new Thing(1);
var b = new Thing(2);
console.log(a + b); // 6 (1 * 2 + 2 * 2)
正如T. J.所说,你不能重载JavaScript中的运算符。但是,您可以利用value eOf
函数编写一个hack,它看起来比每次使用add
之类的函数更好,但对向量施加约束,使x和y介于0和MAX_VALUE之间。以下是代码:
var MAX_VALUE = 1000000;
var Vector = function(a, b) {
var self = this;
//initialize the vector based on parameters
if (typeof(b) == "undefined") {
//if the b value is not passed in, assume a is the hash of a vector
self.y = a % MAX_VALUE;
self.x = (a - self.y) / MAX_VALUE;
} else {
//if b value is passed in, assume the x and the y coordinates are the constructors
self.x = a;
self.y = b;
}
//return a hash of the vector
this.valueOf = function() {
return self.x * MAX_VALUE + self.y;
};
};
var V = function(a, b) {
return new Vector(a, b);
};
然后你可以写这样的等式:
var a = V(1, 2); //a -> [1, 2]
var b = V(2, 4); //b -> [2, 4]
var c = V((2 * a + b) / 2); //c -> [2, 4]
可以用两个数字打包成一个来做向量数学。在解释它是如何工作的之前,让我先展示一个例子:
let a = vec_pack([2,4]);
let b = vec_pack([1,2]);
let c = a+b; // Vector addition
let d = c-b; // Vector subtraction
let e = d*2; // Scalar multiplication
let f = e/2; // Scalar division
console.log(vec_unpack(c)); // [3, 6]
console.log(vec_unpack(d)); // [2, 4]
console.log(vec_unpack(e)); // [4, 8]
console.log(vec_unpack(f)); // [2, 4]
if(a === f) console.log("Equality works");
if(a > b) console.log("Y value takes priority");
我使用的事实是,如果您将两个数字移动X次,然后在将它们移回之前对它们进行加减,您将获得与一开始未移动它们相同的结果。类似地,标量乘除对移动的值对称工作。
JavaScript数字具有52位整数精度(64位浮点数),因此我将一个数字打包到更高可用的26位中,一个打包到更低的中。代码变得有点凌乱,因为我想支持有符号的数字。
function vec_pack(vec){
return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);
}
function vec_unpack(number){
switch(((number & 33554432) !== 0) * 1 + (number < 0) * 2){
case(0):
return [(number % 33554432),Math.trunc(number / 67108864)];
break;
case(1):
return [(number % 33554432)-33554432,Math.trunc(number / 67108864)+1];
break;
case(2):
return [(((number+33554432) % 33554432) + 33554432) % 33554432,Math.round(number / 67108864)];
break;
case(3):
return [(number % 33554432),Math.trunc(number / 67108864)];
break;
}
}
我能看到的唯一缺点是x和y必须在3300万范围内,因为它们必须在26位以内。