javascriptoperatorsoperator-overloading

Javascript: operator overloading


I've been working with JavaScript for a few days now and have got to a point where I want to overload operators for my defined objects.

After a stint on google searching for this it seems you can't officially do this, yet there are a few people out there claiming some long-winded way of performing this action.

Basically I've made a Vector2 class and want to be able to do the following:

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.

Instead I'm having to do this:

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. 

Is there an approach I can take to overload operators in my Vector2 class? As this just looks plain ugly.


Solution

  • As you've found, JavaScript doesn't support operator overloading. The closest you can come is to implement toString (which will get called when the instance needs to be coerced to being a string) and valueOf (which will get called to coerce it to a number, for instance when using + for addition, or in many cases when using it for concatenation because + tries to do addition before concatenation), which is pretty limited. Neither lets you create a Vector2 object as a result. Similarly, Proxy (added in ES2015) lets you intercept various object operations (including property access), but again won't let you control the result of += on Vector instances.


    For people coming to this question who want a string or number as a result (instead of a Vector2), though, here are examples of valueOf and toString. These examples do not demonstrate operator overloading, just taking advantage of JavaScript's built-in handling converting to primitives:

    valueOf

    This example doubles the value of an object's val property in response to being coerced to a primitive, for instance via +:

    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)

    Or with ES2015's 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)

    Or just with objects, no constructors:

    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)

    toString

    This example converts the value of an object's val property to upper case in response to being coerced to a primitive, for instance via +:

    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

    Or with ES2015's 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

    Or just with objects, no constructors:

    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