One approach could be to translate code from the inline operator syntax to method syntax. For this you could use a function that runs once on the functions that you want to have this behaviour.
For the case in your question, you would run it as follows:
const main = Overload.enable(function () {
    var x = new Vector2(10, 10);
    var y = new Vector2(10, 10);
    
    x += y;
    
    console.log(x);
});
main();
Here is how Overload could be defined -- it would be a library to include in your project:
const Overload = {
    // Default for how operators should work:
    binaryOperators: {
        "+"(a, b) { return a + b },
        "-"(a, b) { return a - b },
        "*"(a, b) { return a * b },
        "/"(a, b) { return a / b },
        "%"(a, b) { return a % b },
    },
    unaryOperators: {
        "-"(a) { return -a },
    },
    // Functions that perform operations, being aware of potential overloads
    binary(operator, left, right) {
        return Object(left)[operator] ? Object(left)[operator](right)
             : Object(right)[operator] ? Object(right)[operator](left, true)
             : operators[operator](left, right);
    },
    unary(operator, operand) {
        return Object(operand)[operator] ? Object(operand)[operator]() 
             : operators[operator](operand);
    },
    // Create a new version of a given function 
    // replacing the operators in its source code with calls of the above functions.
    // This has a dependency on the esprima parser
    enable(f) {
        const code = "(" + f + ")";
        const splices = [];
        esprima.parseScript(code, {range:true}, function(node) {
            if (node.type === "BinaryExpression") {
                splices.push([node.range[0], 0, `Overload.binary("${node.operator}",`]);
                splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, ","]);
            } else if (node.type === "AssignmentExpression") {
                splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, 
                        `= Overload.binary("${node.operator.slice(0, -1)}",${code.slice(...node.left.range)},`]);
            } else if (node.type === "UnaryExpression") {
                splices.push([node.range[0], node.operator.length, `Overload.unary("${node.operator}",`]);
            } else return;
            splices.push([node.range[1], 0, ")"]);
        });
        // Apply all collected code changes:
        const arr = [...code];
        for (const [a, b, c] of splices.sort(([a, c], [b, d]) => b - a || d - c)) {
            arr.splice(a, b, c); 
        }
        // Perform an indirect(!!) eval, so that it runs in the global scope (not local)
        // If the input function is a named function, the name is reused for the new function
        return eval?.(arr.join("")); 
    }
};
Here is all of that combined:
const Overload = {binaryOperators: {"+"(a, b) { return a + b },"-"(a, b) { return a - b },"*"(a, b) { return a * b },"/"(a, b) { return a / b },"%"(a, b) { return a % b },},unaryOperators: {"-"(a) { return -a }},binary(operator, left, right) {return Object(left)[operator] ? Object(left)[operator](right): Object(right)[operator] ? Object(right)[operator](left, true): operators[operator](left, right);},unary(operator, operand) {return Object(operand)[operator] ? Object(operand)[operator]() : operators[operator](operand);},enable(f) {const code = "(" + f + ")";const splices = [];esprima.parseScript(code, {range:true}, function(node) {if (node.type === "BinaryExpression") {splices.push([node.range[0], 0, `Overload.binary("${node.operator}",`]);splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, ","]);} else if (node.type === "AssignmentExpression") {splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, `= Overload.binary("${node.operator.slice(0, -1)}",${code.slice(...node.left.range)},`]);} else if (node.type === "UnaryExpression") {splices.push([node.range[0], node.operator.length, `Overload.unary("${node.operator}",`]);} else return;splices.push([node.range[1], 0, ")"]);});const arr = [...code];for (const [a, b, c] of splices.sort(([a, c], [b, d]) => b - a || d - c)) {arr.splice(a, b, c);}return eval?.(arr.join(""));}};
// Demo
class Vector2 extends Array {
    constructor(x, y) {
        super(x, y);
    }
    // The following prototype method intends to overload operator functionality:
    ["+"](other) {
        return new Vector2(this[0] + other[0], this[1] + other[1]);
    }
}
const example = Overload.enable(function () {
    var x = new Vector2(10,10);
    var y = new Vector2(10,10);
    x += y;
    
    console.log(x);
});
example();
<script src="https://unpkg.com/esprima@~4.0/dist/esprima.js"></script>
 
 
Here is an example with an implementation of complex numbers:
const Overload = {binaryOperators: {"+"(a, b) { return a + b },"-"(a, b) { return a - b },"*"(a, b) { return a * b },"/"(a, b) { return a / b },"%"(a, b) { return a % b },},unaryOperators: {"-"(a) { return -a }},binary(operator, left, right) {return Object(left)[operator] ? Object(left)[operator](right): Object(right)[operator] ? Object(right)[operator](left, true): operators[operator](left, right);},unary(operator, operand) {return Object(operand)[operator] ? Object(operand)[operator]() : operators[operator](operand);},enable(f) {const code = "(" + f + ")";const splices = [];esprima.parseScript(code, {range:true}, function(node) {if (node.type === "BinaryExpression") {splices.push([node.range[0], 0, `Overload.binary("${node.operator}",`]);splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, ","]);} else if (node.type === "AssignmentExpression") {splices.push([code.indexOf(node.operator, node.left.range[1]), node.operator.length, `= Overload.binary("${node.operator.slice(0, -1)}",${code.slice(...node.left.range)},`]);} else if (node.type === "UnaryExpression") {splices.push([node.range[0], node.operator.length, `Overload.unary("${node.operator}",`]);} else return;splices.push([node.range[1], 0, ")"]);});const arr = [...code];for (const [a, b, c] of splices.sort(([a, c], [b, d]) => b - a || d - c)) {arr.splice(a, b, c);}return eval?.(arr.join(""));}};
class Complex {
    constructor(real, img) {
        this.real = real;
        this.img = img;
    }
    toString() {
        if (!this.img) return `${this.real}`;
        if (!this.real) return `${this.img}i`;
        return `${this.real}${this.img < 0 ? "" : "+"}${this.img}i`;
    }
    static cast(other) {
        if (other instanceof Complex || other == null) return other; // Pass-thru
        if (Object(other) !== other) return new Complex(Number(other), 0); // Primitive
        if (other.length === 2) return new Complex(other[0], other[1]); // Array-like
        throw "Cannot cast this to Complex";
    }
    // The following prototype methods will override operator functionality:
    ["+"](other) {
        other = Complex.cast(other);
        return new Complex(this.real + other.real, this.img + other.img);
    }
    ["-"](other, reversedOperands=false) {
        // Unary minus?
        if (arguments.length === 0) return new Complex(-this.real, -this.img);
        // Binary minus:
        other = Complex.cast(other);
        return reversedOperands ? this["-"]()["+"](other) : other["-"]()["+"](this);
    }
    ["*"](other) {
        other = Complex.cast(other);
        return new Complex(this.real * other.real - this.img * other.img, this.real * other.img + this.img * other.real);
    }
    ["/"](other, reversedOperands=false) {
        other = Complex.cast(other);
        if (reversedOperands) return other["/"](this);
        const denom = other.real * other.real + other.img * other.img;
        return new Complex((this.real * other.real + this.img * other.img) / denom,
                           (this.img * other.real - this.real * other.img) / denom);
    }
}
// Decorate the function(s) that need operator overrides:
const example = Overload.enable(function (a, b) {
    return 8 * (a - b) / (a + b);
});
const result = example(new Complex(10, 6), new Complex(4, 8))
console.log("result: "  + result);
<script src="https://unpkg.com/esprima@~4.0/dist/esprima.js"></script>
 
 
Remarks:
This approach has no limitation on the number of operations that are performed in one expression.
For assignment operators, such as +=, this solution silently assumes that there are no side-effects being performed in the left-hand side of the assignment. If this is the case, the converted code would execute that side effect twice, like in this example:
a[i++] += a[j];
This would be executed as:
a[i++] = Overload.binary("+", a[i++], a[j]);
...and so you see i gets incremented twice. To avoid this, make sure that the left-hand side has no side effects. If the accessed property of a is not a getter, then instead do:
a[i] += a[j];
i++;