Javascript:运算符重载
我已经使用JavaScript几天了,现在我已经到了一个需要为我定义的对象重载操作符的地步 在谷歌搜索了一段时间后,似乎你还不能正式这么做,但是有一些人声称他们用了一些冗长的方式来完成这个动作 基本上,我制作了一个Vector2类,希望能够完成以下操作:Javascript:运算符重载,javascript,operators,operator-overloading,Javascript,Operators,Operator Overloading,我已经使用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
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
(当需要将实例强制为字符串时将调用该函数)和valueOf
(例如,当使用+
进行加法时,或者在许多情况下,当使用它进行串联时,因为+
尝试在串联之前进行加法),这将被调用以将其强制为数字,这是非常有限的。这两种方法都不允许您创建矢量2
对象
对于想得到字符串或数字(而不是
Vector2
)的人来说但是,下面是valueOf
和toString
的示例。这些示例没有演示运算符重载,只是利用JavaScript的内置处理转换为原语:
valueOf
此示例将对象的val
属性的值加倍,以响应被强制到原语的情况,例如通过+
:
函数对象(val){
this.val=val;
}
Thing.prototype.valueOf=函数(){
//在这里,我只是加倍它;你实际上会做你的longAdd的事情
返回此值。val*2;
};
var a=新事物(1);
var b=新事物(2);
console.log(a+b);/6(1*2+2*2)
正如T.J.所说,你不能在JavaScript中重载运算符。但是你可以利用valueOf
函数编写一个hack,它看起来比每次使用add
之类的函数要好,但是对向量施加了x和y介于0和MAX_值之间的约束。下面是代码:
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]
FYI paper.js通过创建PaperScript解决了这个问题,PaperScript是一种自包含的、范围明确的javascript,具有向量的运算符重载,然后将其处理回javascript
但是paperscript文件需要专门指定并进行处理。实际上,JavaScript有一种变体确实支持运算符重载。ExtendScript是Adobe应用程序(如Photoshop和Illustrator)使用的脚本语言,它确实具有运算符重载。在它中,您可以编写:
Vector2.prototype["+"] = function( b )
{
return new Vector2( this.x + b.x, this.y + b.y );
}
var a = new Vector2(1,1);
var b = new Vector2(2,2);
var c = a + b;
“Adobe Extendscript JavaScript工具指南”(当前版本)对此进行了更详细的描述。该语法显然是基于ECMAScript标准的一份草案(现已废弃很久)。可以将两个数字组合成一个数字来进行向量计算。在解释其工作原理之前,让我先展示一个示例:
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位,将一个数字打包成较低的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;
}
}
功能向量包(vec){
返回向量[1]*67108864+(向量[0]<0?33554432 |向量[0]:向量[0]);
}
功能向量解包(编号){
开关(((编号&33554432)!==0)*1+(编号<0)*2){
案例(0):
return[(编号%33554432),Math.trunc(编号/67108864)];
打破
案例(1):
return[(数字%33554432)-33554432,Math.trunc(数字/67108864)+1];
打破
案例(2):
返回[((数字+33554432)%33554432)+33554432)%33554432,数学四舍五入(数字/67108864)];
打破
案例(3):
return[(编号%33554432),Math.trunc(编号/67108864)];
打破
}
}
我能看到的唯一缺点是x和y必须在+-3300万范围内,因为它们必须分别在26位以内。有趣的是,它也是实验性的库。它在定义的上下文中进行重载(回调函数)只有。我们可以使用React-like-hook在每次迭代中使用
valueOf
方法的不同值来计算箭头函数
const a = Vector2(1, 2) // [1, 2]
const b = Vector2(2, 4) // [2, 4]
const c = Vector2(() => (2 * a + b) / 2) // [2, 4]
// There arrow function will iterate twice
// 1 iteration: method valueOf return X component
// 2 iteration: method valueOf return Y component
常量向量2=(函数(){
设索引=-1
返回函数(x,y){
如果(x的类型==='函数'){
常数计算=x
索引=0,x=calc()
索引=1,y=calc()
索引=-1
}
返回Object.assign([x,y]{
价值(){
返回索引==-1?this.toString():this[index]
},
toString(){
返回`[${this[0]},${this[1]}]`
},
len(){
返回Math.sqrt(此[0]**2+此[1]**2)
}
})
}
})()
常数a=Vector2(1,2)
常数b=Vector2(2,4)
console.log('a='+a)//a=[1,2]
log(`b=${b}`)//b=[2,4]
常数c=Vector2(()=>(2*a+b)/2)/[2,4]
a[0]=12
常数d=Vector2(()=>(2*a+b)/2)/[13,4]
const normalized=Vector2(()=>d/d.len())/[0.955…,0.294…]
log(c,d,normalized)虽然不是这个问题的确切答案,但是可以使用ES6符号实现一些python神奇的方法
[Symbol.toPrimitive]()
方法不允许您暗示调用Vector.add()
,但允许您使用诸如Decimal()+int
之类的语法
class AnswerToLifeAndUniverseAndEverything {
[Symbol.toPrimitive](hint) {
if (hint === 'string') {
return 'Like, 42, man';
} else if (hint === 'number') {
return 42;
} else {
// when pushed, most classes (except Date)
// default to returning a number primitive
return 42;
}
}
}