Surcharger les opérateurs arithmétiques en JavaScript?

C'est la meilleure façon pour moi de formuler cette question, étant donné cette définition JavaScript "class":

var Quota = function(hours, minutes, seconds){
    if (arguments.length === 3) {
        this.hours = hours;
        this.minutes = minutes;
        this.seconds = seconds;

        this.totalMilliseconds = Math.floor((hours * 3600000)) + Math.floor((minutes * 60000)) + Math.floor((seconds * 1000));
    }
    else if (arguments.length === 1) {
        this.totalMilliseconds = hours;

        this.hours = Math.floor(this.totalMilliseconds / 3600000);
        this.minutes = Math.floor((this.totalMilliseconds % 3600000) / 60000);
        this.seconds = Math.floor(((this.totalMilliseconds % 3600000) % 60000) / 1000);
    }

    this.padL = function(val){
        return (val.toString().length === 1) ? "0" + val : val;
    };

    this.toString = function(){
        return this.padL(this.hours) + ":" + this.padL(this.minutes) + ":" + this.padL(this.seconds);
    };

    this.valueOf = function(){
        return this.totalMilliseconds;
    };
};

et le code d'installation d'essai suivant:

var q1 = new Quota(23, 58, 50);
var q2 = new Quota(0, 1, 0);
var q3 = new Quota(0, 0, 10);

console.log("Quota 01 is " + q1.toString());    // Prints "Quota 01 is 23:58:50"
console.log("Quota 02 is " + q2.toString());    // Prints "Quota 02 is 00:01:00"
console.log("Quota 03 is " + q3.toString());    // Prints "Quota 03 is 00:00:10"

est-il possible de créer implicitement q4 comme un objet Quota en utilisant l'opérateur addition comme suit...

var q4 = q1 + q2 + q3;
console.log("Quota 04 is " + q4.toString());    // Prints "Quota 04 is 86400000"

plutôt que d'y recourir...

var q4 = new Quota(q1 + q2 + q3);
console.log("Quota 04 is " + q4.toString());    // Prints "Quota 04 is 24:00:00"

si ce n'est pas ce qui est le mieux recommandations pratiques dans ce domaine pour rendre les objets JavaScript numériques personnalisés composables par l'intermédiaire des opérateurs arithmétiques?

62
demandé sur Peter McG 2009-10-28 02:38:04

11 réponses

autant que je sache, Javascript (du moins tel qu'il existe maintenant) ne supporte pas la surcharge de l'opérateur.

le mieux que je puisse suggérer est une méthode de classe pour faire de nouveaux objets de quota de plusieurs autres. Voici un exemple rapide de ce que je veux dire:

// define an example "class"
var NumClass = function(value){
    this.value = value;
}
NumClass.prototype.toInteger = function(){
    return this.value;
}

// Add a static method that creates a new object from several others
NumClass.createFromObjects = function(){
    var newValue = 0;
    for (var i=0; i<arguments.length; i++){
        newValue += arguments[i].toInteger();
    }
    return new this(newValue)
}

et l'utiliser comme:

var n1 = new NumClass(1);
var n2 = new NumClass(2);
var n3 = new NumClass(3);

var combined = NumClass.createFromObjects(n1, n2, n3);
33
répondu Dan 2009-10-28 01:22:22

malheureusement non.

pour les retombées, si vous avez arrangé les valeurs de retour, vous pouvez utiliser la méthode de chaînage

var q4 = q1.plus(p2).plus(q3);
19
répondu Justin Love 2009-10-28 01:19:46

puisque tout le monde a voté contre mon autre réponse, je voulais afficher le code de validation de principe qui fonctionne en fait comme prévu.

cela a été testé dans chrome et IE.

//Operator Overloading

var myClass = function () {

//Privates

var intValue = Number(0),
    stringValue = String('');

//Publics
this.valueOf = function () {
    if (this instanceof myClass) return intValue;
    return stringValue;
}

this.cast = function (type, call) {
    if (!type) return;
    if (!call) return type.bind(this);
    return call.bind(new type(this)).call(this);
}

}

//Derived class
var anotherClass = function () {

//Store the base reference
this.constructor = myClass.apply(this);

var myString = 'Test',
    myInt = 1;

this.valueOf = function () {
    if (this instanceof myClass) return myInt;
    return myString;
}

}


//Tests

var test = new myClass(),
anotherTest = new anotherClass(),
composed = test + anotherTest,
yaComposed = test.cast(Number, function () {
    return this + anotherTest
}),
yaCComposed = anotherTest.cast(Number, function () {
    return this + test;
}),
t = test.cast(anotherClass, function () {
    return this + anotherTest
}),
tt = anotherTest.cast(myClass, function () {
    return this + test;
});

debugger;

Si quelqu'un serait donc de nature à donner une explication technique POURQUOI ce n'est pas assez bon, je serais heureux de l'entendre!

13
répondu Jay 2012-05-09 01:18:49

deuxième suggestion:

var q4 = Quota.add(q1, q2, q3);
7
répondu Justin Love 2009-10-28 01:20:27

je suis récemment tombé sur cet article: http://www.2ality.com/2011/12/fake-operator-overloading.html .

il décrit comment vous pouvez redéfinir la valeur de la méthode sur les objets pour faire quelque chose comme la surcharge de l'opérateur dans javascript. Il semble que vous ne pouvez effectuer que des opérations de mutation sur les objets opérés, donc ça ne ferait pas ce que vous voulez. Son intéressant néanmoins tho.

5
répondu B T 2012-01-29 20:34:41

vous pouvez convertir implicite en entier ou chaîne, vos objets

Les objets

ne sont convertis qu'implicitement si JavaScript attend un nombre ou une chaîne. Dans le premier cas, la conversion s'effectue en trois étapes:

1.- Call valueOf (). Si le résultat est primitif (pas un objet) alors utilisez-le et convertissez-le en un nombre.

2.- Dans le cas contraire, l'appel de toString(). Si le résultat est primitif, de l'utiliser et de le convertir en nombre.

3.- Sinon, lancer une exception TypeError. Exemple pour l'étape 1:

3 * { value of: function () { return 5 } }

si JavaScript convertit en chaîne, les étapes 1 et 2 sont échangées: toString() est essayé en premier, valueOf() en second.

http://www.2ality.com/2013/04/quirk-implicit-conversion.html

4
répondu Luis Naves 2015-04-23 02:39:55

papier.js le fait, par exemple avec l'ajout de point ( docs ):

var point = new Point(5, 10);
var result = point + 20;
console.log(result); // {x: 25, y: 30}

mais il le fait en utilisant son propre custom script parser .

4
répondu Izhaki 2016-12-27 21:52:39

Je ne suis pas sûr pourquoi les gens continuent à répondre à cette question avec non!

il y a absolument une voie que je vais esquisser avec un très petit script que vous n'avez pas besoin D'être John Resig pour comprendre...

avant que je le fasse, je vais aussi dire que dans JavaScript la façon dont votre constructeur aurait fonctionné est en vérifiant les tableaux ou en itérant les 'arguments' littéral.

par exemple, Dans mon constructeur de ma classe je itérer la arugments, de déterminer le type de sous-jacents arugments et les traiter de manière intelligente.

cela signifie que si vous avez passé un tableau je itérerai les éléments pour trouver un tableau et puis itérerai le tableau pour faire un traitement supplémentaire en fonction du type de l'élément dans le tableau.

E. G. - >new someClass ([instanceA, instanceB, instanceC])

cependant vous les gars êtes à la recherche d'une approche plus "C" Style de l'opérateur surcharge qui peut en fait être réalisée contrairement à la croyance populaire.

Voici une classe que j'ai créée en utilisant MooTools qui honore la surcharge de l'opérateur. En JavaScript simple vieux vous utiliseriez juste la même méthode de toString seulement l'attacher au prototype de l'instance directement.

ma principale raison pour afficher cette approche est en raison du texte que je lis sans cesse qui stipule que cette fonctionnalité est" impossible " à émuler. Rien n'est impossible que suffisamment difficile et je vais l'afficher ci-dessous...

 //////

debugger;

//Make a counter to prove I am overloading operators
var counter = 0;

//A test class with a overriden operator
var TestClass = new Class({
    Implements: [Options, Events],
    stringValue: 'test',
    intValue: 0,
    initialize: function (options) {
        if (options && options instanceof TestClass) {
            //Copy or compose
            this.intValue += options.intValue;
            this.stringValue += options.stringValue;
        } else {
            this.intValue = counter++;
        }
    },
    toString: function () {
        debugger;
        //Make a reference to myself
        var self = this;
        //Determine the logic which will handle overloads for like instances
        if (self instanceof TestClass) return self.intValue;
        //If this is not a like instance or we do not want to overload return the string value or a default.
        return self.stringValue;
    }
});

//Export the class
window.TestClass = TestClass;

//make an instance
var myTest = new TestClass();

//make another instance
var other = new TestClass();

//Make a value which is composed of the two utilizing the operator overload
var composed = myTest + other;

//Make a value which is composed of a string and a single value
var stringTest = '' + myTest;

//////

L'affichage le plus récent de cette nomenclature a été observé à la page de documentation de XDate: http://arshaw.com/xdate /

dans ce cas, je crois que c'était encore plus facile, il aurait pu utiliser le prototype de L'objet Date pour obtenir le même.

néanmoins la méthode que j'ai donnée comme exemple qui devrait représenter ce style d'utilisation pour les autres.

Edit:

j'ai une mise en œuvre complète ici:

http://netjs.codeplex.com /

avec d'autres goodies.

2
répondu Jay 2013-02-22 19:58:53

en plus de ce qui a déjà été dit: supérieur .valueOf () peut aider à produire une surcharge de l'opérateur assez puissante. Dans la preuve de concept les Doigts.js lib vous pouvez ajouter des écouteurs d'événements dans le style .NET:

function hi() { console.log("hi") }
function stackoverflow() { console.log("stackoverflow") }
function bye() { console.log("bye") }

on(yourButton).click += hi + stackoverflow;
on(yourButton).click -= hi - bye;

L'idée principale est de remplacer temporairement valueOf lorsque on () est appelé:

const extendedValueOf = function () {
    if (handlers.length >= 16) {
        throw new Error("Max 16 functions can be added/removed at once using on(..) syntax");
    }

    handlers.push(this); // save current function

    return 1 << ((handlers.length - 1) * 2); // serialize it as a number.
};
Le nombre

retourné peut alors être dé-sérialisé de nouveau en fonction en utilisant le tableau handlers. En plus, c'est possible extraire les valeurs de bits de la valeur finale (func1 + func2-func3) pour que vous puissiez comprendre efficacement quelles fonctions ont été ajoutées et quelles fonctions ont été supprimées.

vous pouvez consulter les sources sur GitHub et jouer avec démo ici .

explication complète existe dans cet article (c'est pour AS3, difficile car c'est ecmascript il fonctionnera pour JS soit).

1
répondu average Joe 2014-07-28 11:19:30

j'ai fait un script qui fait la surcharge de l'opérateur en JavaScript. Ce n'était pas simple à faire fonctionner, donc il y a quelques bizarreries. Je vais croiser les mises en garde ici à partir de la page du projet, sinon vous pouvez trouver le lien En bas de page:

  • les résultats du calcul doivent être transmis à un nouvel objet, donc au lieu de (p1 + p2 + p3) vous devez faire un nouveau point(p1 + p2 + p3), (étant donné que votre objet défini par l'utilisateur est appelé "point").

  • ,+, -, * et / sont pris en charge, le cinquième de l'arithmétique opperator % ne l'est pas. La coercition aux chaînes (""+p1) et les comparaisons (p1 == p2) ne fonctionneront pas comme prévu. De nouvelles fonctions devraient être construites à ces fins si nécessaire, comme (p1.val = = p2.Val.)

  • enfin, les ressources informatiques nécessaires pour calculer la réponse augmentent quadratiquement avec le nombre de termes. Par conséquent seulement 6 termes est autorisé dans un chaîne de calcul par défaut (bien que cela puisse être augmenté). Pour des chaînes de calcul plus longues que celle-là, diviser les calculs vers le haut, comme: nouveau point(nouveau point (p1 + p2 + p3 + p4 + p5 + p6) + nouveau point (p7 + p8 + p9 + p10 + p11 + p12))

Le Github page .

1
répondu Stuffe 2016-11-18 16:41:17

pour certains cas d'utilisation limitée vous pouvez avoir l'opérateur "effets de surcharge":

function MyIntyClass() {
    this.valueOf = function() { return Math.random(); }
}
var a = new MyIntyClass();
var b = new MyIntyClass();
a < b
false

a + b
0.6169137847609818

[a, b].sort() // O(n^2) ?
[myClass, myClass]

function MyStringyClass() {
    this.valueOf = function() { return 'abcdefg'[Math.floor(Math.random()*7)]; }
}
c = new MyStringyClass();
'Hello, ' + c + '!'
Hello, f!

le code ci-dessus est libre à utiliser sous la licence MIT. YMMV.

-1
répondu Jason Boyd 2015-09-23 10:21:23