Lee*_*ley 72 javascript operator-overloading operators
我已经使用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.
Run Code Online (Sandbox Code Playgroud)
相反,我必须这样做:
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.
Run Code Online (Sandbox Code Playgroud)
我可以采用一种方法来重载Vector2类中的运算符吗?因为这看起来很丑陋.
T.J*_*der 84
正如您所发现的,JavaScript不支持运算符重载.你可以来的最接近的是实现toString
(当实例需要被强制为字符串时valueOf
将被调用)和(将被调用以将其强制转换为数字,例如当+
用于添加时,或者在许多情况下使用它进行连接,因为+
在连接之前尝试进行添加),这是非常有限的.也不允许您创建Vector2
对象作为结果.
对于那些想要一个字符串或数字作为结果(而不是一个Vector2
)的人来说,这里有valueOf
和的例子toString
.这些示例不会演示运算符重载,只是利用JavaScript的内置处理转换为基元:
valueOf
此示例将对象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)
Run Code Online (Sandbox Code Playgroud)
或者使用ES2015 class
:
class Thing {
constructor(val) {
this.val = val;
}
valueOf() {
return this.val * 2;
}
}
const a = new Thing(1);
const b = new Thing(2);
console.log(a + b); // 6 (1 * 2 + 2 * 2)
Run Code Online (Sandbox Code Playgroud)
或者只是对象,没有构造函数:
var thingPrototype = {
valueOf: function() {
return this.val * 2;
}
};
var a = Object.create(thingPrototype);
a.val = 1;
var b = Object.create(thingPrototype);
b.val = 2;
console.log(a + b); // 6 (1 * 2 + 2 * 2)
Run Code Online (Sandbox Code Playgroud)
toString
此示例将对象val
属性的值转换为大写,以响应被强制转换为基元,例如通过+
:
function Thing(val) {
this.val = val;
}
Thing.prototype.toString = function() {
return this.val.toUpperCase();
};
var a = new Thing("a");
var b = new Thing("b");
console.log(a + b); // AB
Run Code Online (Sandbox Code Playgroud)
或者使用ES2015 class
:
class Thing {
constructor(val) {
this.val = val;
}
toString() {
return this.val.toUpperCase();
}
}
const a = new Thing("a");
const b = new Thing("b");
console.log(a + b); // AB
Run Code Online (Sandbox Code Playgroud)
或者只是对象,没有构造函数:
var thingPrototype = {
toString: function() {
return this.val.toUpperCase();
}
};
var a = Object.create(thingPrototype);
a.val = "a";
var b = Object.create(thingPrototype);
b.val = "b";
console.log(a + b); // AB
Run Code Online (Sandbox Code Playgroud)
mrv*_*mir 19
正如TJ所说,你不能在JavaScript中重载运算符.但是你可以利用这个valueOf
函数来编写一个看起来比add
每次使用函数更好的hack ,但是对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);
};
Run Code Online (Sandbox Code Playgroud)
然后你可以写这样的方程式:
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]
Run Code Online (Sandbox Code Playgroud)
J. *_*son 12
实际上,有一种 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;
Run Code Online (Sandbox Code Playgroud)
这在“Adobe Extendscript JavaScript 工具指南”(此处为当前链接)中有更详细的描述。语法显然是基于 ECMAScript 标准的(现已废弃很久的)草案。
Stu*_*ffe 12
可以将两个数字合二为一进行矢量数学运算。在解释它是如何工作的之前,让我先举一个例子:
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");
Run Code Online (Sandbox Code Playgroud)
我使用的事实是,如果您将两个数字移位 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;
}
}
Run Code Online (Sandbox Code Playgroud)
我能看到的唯一缺点是 x 和 y 必须在 +-3300 万的范围内,因为它们每个都必须在 26 位以内。
虽然不是问题的确切答案,但可以使用 ES6 符号实现一些 python __magic__ 方法
方法[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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
FYI paper.js通过创建PaperScript解决了这个问题,PaperScript是一个包含运算符重载向量的自包含的scoped javascript,然后将其处理回javascript.
但是需要明确指定和处理起稿文件.
我们可以使用类似 React 的 HooksvalueOf
在每次迭代中使用来自方法的不同值来评估箭头函数。
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
库@js-basics/vector对 Vector3 使用相同的想法。
我写了一个库,利用一堆邪恶的黑客在原始 JS 中做到这一点。它允许这样的表达式。
复数:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
自动微分:
让f(x) = x^3 - 5x
:
>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
现在将它映射到一些值上:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
IE f'(x) = 3x^2 - 5.
多项式:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
对于您的特定问题,您将Vector2
使用库定义一个函数(或者可能更短的函数),然后编写x = Vector2()(x + y);
https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7
有趣的是实验库operator-overloading-js。它仅在定义的上下文(回调函数)中进行重载。