web-dev-qa-db-fra.com

Javascript: surcharge de l'opérateur

Je travaille avec JavaScript depuis quelques jours maintenant et je suis sur le point de surcharger les opérateurs pour mes objets définis.

Après un passage sur google à la recherche de cela, il semble que vous ne puissiez pas le faire officiellement, mais il y a quelques personnes qui réclament une manière longue de réaliser cette action.

En gros, j'ai créé une classe Vector2 et je veux pouvoir effectuer les tâches suivantes:

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.

Au lieu de cela, je dois faire ceci:

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. 

Existe-t-il une approche que je peux adopter pour surcharger les opérateurs de ma classe Vector2? Comme cela semble tout simplement moche.

66
Lee Brindley

Comme vous l'avez constaté, JavaScript ne prend pas en charge la surcharge d'opérateurs. Le plus proche que vous puissiez faire est d'implémenter toString (qui sera appelé lorsque l'instance doit être forcée à devenir une chaîne) et valueOf (qui sera appelé pour la contraindre à un nombre, par exemple en utilisant + pour l'addition, ou dans de nombreux cas lors de son utilisation pour la concaténation, car + essaie de faire l’addition avant la concaténation), ce qui est assez limité. Ni vous permet de créer un Vector2 objet en conséquence.


Pour les personnes qui répondent à cette question et qui souhaitent obtenir une chaîne ou un nombre (au lieu d’un Vector2), cependant, voici des exemples de valueOf et toString. Ces exemples ne le font pas démontrent la surcharge de l'opérateur, en tirant simplement parti de la gestion intégrée de JavaScript convertissant les primitives:

valueOf

Cet exemple double la valeur de la propriété val d'un objet en réponse à une contrainte sur une primitive, par exemple 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)

Ou avec class de ES2015:

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)

Ou simplement avec des objets, pas de constructeurs:

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

Cet exemple convertit la valeur de la propriété val d'un objet en majuscules en réponse à une contrainte sur une primitive, par exemple 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

Ou avec class de ES2015:

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

Ou simplement avec des objets, pas de constructeurs:

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
80
T.J. Crowder

Comme T.J. dit, vous ne pouvez pas surcharger les opérateurs en JavaScript. Cependant, vous pouvez tirer parti de la fonction valueOf pour écrire un hack qui a meilleure apparence que d'utiliser des fonctions comme add à chaque fois, mais impose au vecteur la contrainte que x et y soient compris entre 0 et VALEUR MAX. Voici le code:

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);
};

Ensuite, vous pouvez écrire des équations comme ceci:

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]
19
user2259659

FYI paper.js résout ce problème en créant PaperScript, un javascript autonome et étendu avec une surcharge des vecteurs par l’opérateur, qu’il traite ensuite en javascript.

Mais les fichiers manuscrits doivent être spécifiés et traités comme tels.

6
Joshua Penman

Il est possible de faire des mathématiques vectorielles avec deux nombres réunis dans un. Laissez-moi d'abord montrer un exemple avant d'expliquer comment cela fonctionne:

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");

J'utilise le fait que si vous décalez un peu deux chiffres X, puis que vous les additionnez ou les soustrayez avant de les décaler, vous obtiendrez le même résultat que si vous ne les aviez pas décalés pour commencer. De même, la multiplication et la division scalaires fonctionnent de manière symétrique pour les valeurs décalées.

Un nombre JavaScript a une précision entière de 52 bits (flotteurs de 64 bits), je vais donc en insérer un dans les 26 bits disponibles les plus élevés et dans le nombre inférieur. Le code est un peu plus compliqué parce que je voulais prendre en charge les numéros signés.

vec_unpack = function(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;
    }
}

Le seul inconvénient que je puisse constater à ce propos est que les valeurs x et y doivent être comprises entre + et -33 millions, car elles doivent tenir dans 26 bits chacune.

0
Stuffe

En fait, il existe une variante de JavaScript qui prend en charge la surcharge des opérateurs. ExtendScript, le langage de script utilisé par les applications Adobe telles que Photoshop et Illustrator, est surchargé par les opérateurs. Vous pouvez y écrire:

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;

Ceci est décrit plus en détail dans le "Guide des outils JavaScript d'Adobe Extendscript" (current lien ici ). La syntaxe était apparemment basée sur un brouillon (maintenant abandonné depuis longtemps) du standard ECMAScript.

0
J. Peterson