Javascript:运算符重载

IT技术 javascript operators operator-overloading
2021-01-13 00:32:19

我已经使用 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 类中的运算符重载?因为这看起来很丑。

6个回答

正如您所发现的,JavaScript 不支持运算符重载。最接近的是实现toString(当需要将实例强制为字符串时valueOf将调用它)和(将被调用以将其强制为数字,例如在+用于加法时,或在许多情况下使用它进行连接是因为+尝试在连接之前进行加法),这是非常有限的。两者都不允许您因此创建Vector2对象。同样,Proxy(在 ES2015 中添加)允许您拦截各种对象操作(包括属性访问),但同样不会让您控制+=onVector实例的结果


但是,对于想要字符串或数字作为结果(而不是Vector2)的人来回答这个问题,这里是valueOf和 的示例toString这些示例演示运算符重载,只是利用 JavaScript 的内置处理转换为原语:

valueOf

此示例将对象val属性的值加倍以响应被强制转换为原语,例如通过+

或者使用 ES2015 的class

或者只使用对象,没有构造函数:

toString

本示例将对象val属性的值转换为大写,以响应被强制转换为原语,例如通过+

或者使用 ES2015 的class

或者只使用对象,没有构造函数:

@AndersonGreen - 这并没有真正重载运算符.例如,它也会影响),但是是的,您可以拦截各种对象操作(包括属性访问)。这对我们没有帮助+=,但是...
2021-03-16 00:32:19
@SeanLetendre:是的。><>=、 和<=(但不是=====!=、 或!==)使用抽象关系比较操作,它ToPrimitive与提示“数字”一起使用。Date对象上,这会导致getTime返回的数字(自 The-Epoch 以来的毫秒值)。
2021-03-19 00:32:19
也可以使用对象重载[]运算符Proxy
2021-03-21 00:32:19
虽然 JS 本身不支持它,但如今使用自定义功能扩展 JS 并将其转换回普通 JS 是很常见的,例如,SweetJS旨在解决这个问题。
2021-04-01 00:32:19
Date上的比较运算符是否使用valueOf? 例如,您可以这样做date2 > date1,如果date2是在date1.
2021-04-06 00:32: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);
};

然后你可以写出这样的方程:

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]
@IanBrindley OP 想要重载一个运算符,这显然意味着他计划编写这样一个函数。OP 担心必须调用“add”,这是不自然的;在数学上,我们用+符号表示向量加法这是一个很好的答案,展示了如何避免为准数字对象调用不自然的函数名称。
2021-03-15 00:32:19
请注意,这将在将两个向量相乘时返回意外结果(而不是给出错误)。此外,坐标必须是整数。
2021-03-20 00:32:19
@Kittsil 问题表明我已经在使用添加功能。虽然上面的函数并不是一个糟糕的函数,但它并没有解决这个问题,所以我同意 Ian 的观点。
2021-03-31 00:32:19
您基本上只是为 OP 的add方法编写了代码......他们不想做的事情。
2021-04-05 00:32:19
到目前为止,这是唯一可能的方法。我们对+运算符的唯一灵活性是能够返回 aNumber作为其中一个操作数的替代。因此,任何适用于Object实例的添加功能都必须始终将对象编码为Number,并最终对其进行解码。
2021-04-10 00:32:19

实际上,有一种 JavaScript 变体确实支持运算符重载。ExtendScript 是 Adob​​e 应用程序(例如 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 标准的(现已长期废弃)草案。

扩展脚本 != JavaScript
2021-03-17 00:32:19
为什么 ExtendScript 答案被低估而 PaperScript 答案被高票?恕我直言,这个答案也很好。
2021-04-06 00:32:19

可以将两个数字合二为一进行矢量数学运算。在解释它是如何工作的之前,让我先展示一个例子:

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 位以内。

@Disgusting 嗯抱歉,看起来我忘记添加了......现在已经修复了:)
2021-03-23 00:32:19
vec_pack 的定义在哪里?
2021-03-29 00:32:19

仅供参考 paper.js 通过创建 PaperScript 解决了这个问题,PaperScript 是一个自包含的、作用域的 javascript,具有向量的运算符重载,然后它会处理回 javascript。

但是paperscript文件需要专门指定和处理。