Pregunta

Esta es la mejor manera en que puedo pensar en formular esta pregunta, dada esta "clase" de JavaScript definición:

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

y el siguiente código de configuración de prueba:

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"

¿Hay alguna forma de crear implícitamente q4 como un objeto Quota utilizando el operador de suma de la siguiente manera ...

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

en lugar de recurrir a ...

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

Si no, ¿cuáles son las recomendaciones de mejores prácticas en esta área para hacer que los objetos numéricos personalizados de JavaScript sean componibles mediante los operadores aritméticos?

¿Fue útil?

Solución

Hasta donde yo sé, Javascript (al menos como existe ahora) no admite la sobrecarga del operador.

Lo mejor que puedo sugerir es un método de clase para hacer nuevos objetos de cuota de varios otros. Aquí hay un ejemplo rápido de lo que quiero decir:

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

y úsalo como:

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

var combined = NumClass.createFromObjects(n1, n2, n3);

Otros consejos

Desafortunadamente no.

Para los retrocesos, si organizó los valores de retorno, podría usar el encadenamiento de métodos

var q4 = q1.plus(p2).plus(q3);

Como todos rechazaron mi otra respuesta, quería publicar un código de prueba de concepto que, de hecho, funciona según lo previsto.

Esto ha sido probado en Chrome e 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 alguien fuera tan amable de dar una explicación técnica POR QUÉ esto no es lo suficientemente bueno, ¡me encantaría escucharlo!

Segunda sugerencia:

var q4 = Quota.add(q1, q2, q3);

Recientemente me encontré con este artículo: http: //www.2ality .com / 2011/12 / fake-operator-overloading.html .

Describe cómo puede redefinir el método valueOf en objetos para hacer algo como la sobrecarga del operador en javascript. Parece que realmente solo puedes realizar operaciones de mutación en los objetos que se operan, por lo que no haría lo que quieres. No obstante, es interesante.

puedes convertir implícitamente a entero o cadena, tus objetos

Los objetos solo se convierten implícitamente si JavaScript espera un número o una cadena. En el primer caso, la conversión toma tres pasos:

1.- Llamada valueOf (). Si el resultado es primitivo (no un objeto), úselo y conviértalo en un número.

2.- De lo contrario, llame a toString (). Si el resultado es primitivo, úselo y conviértalo en un número.

3.- De lo contrario, arroje un TypeError. Ejemplo para el paso 1:

  

3 * {valueOf: function () {return 5}}

Si JavaScript se convierte en cadena, los pasos 1 y 2 se intercambian: toString () se intenta primero, valueOf () segundo.

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

Paper.js lo hace, por ejemplo, con la suma de puntos ( docs ):

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

Pero lo hace usando su propio script personalizado analizador .

¡No estoy seguro de por qué la gente continúa respondiendo esta pregunta con un no!

Hay una forma absolutamente clara que describiré con un script muy pequeño que no tiene que ser John Resig para entender ...

Antes de hacerlo, también declararé que en JavaScript la forma en que su constructor habría funcionado es verificando las matrices o iterando el literal 'argumentos'.

p. En mi constructor de mi 'clase', iteraría los argumentos, determinaría el tipo de los argumentos subyacentes y los procesaría de manera inteligente.

Esto significa que si pasa una matriz, iteraría las disposiciones para encontrar una matriz y luego la iteraría para realizar un procesamiento adicional dependiendo del tipo de elemento en la matriz.

Por ejemplo. - > nuevo someClass ([instanciaA, instanciaB, instanciaC])

Sin embargo, ustedes están buscando una "C" más enfoque de estilo para la sobrecarga del operador que en realidad se puede lograr en contra de la creencia popular.

Aquí hay una clase que he creado usando MooTools que respeta la sobrecarga del operador. En JavaScript antiguo, simplemente utilizaría el mismo método toString, solo lo adjuntaría directamente al prototipo de instancia.

Mi razón principal para mostrar este enfoque es debido al texto que leo continuamente que dice que esta funcionalidad es "imposible". emular. Nada es imposible solo lo suficientemente difícil y lo mostraré a continuación ...

 //////

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;

//////

La visualización más reciente de esta nomenclatura se observó en la página de documentación de XDate: http://arshaw.com/xdate/

En este caso, creo que en realidad fue aún más fácil, podría haber usado el prototipo del objeto Date para lograr lo mismo.

Sin embargo, el método que he dado como un ejemplo que debe representar este estilo de utilización para otros.

Editar:

Tengo una implementación completa aquí:

http://netjs.codeplex.com/

Junto con otras cosas.

Hice un script que sobrecarga al operador en JavaScript. No fue sencillo hacer el trabajo, por lo que hay algunas peculiaridades. Voy a publicar las advertencias aquí desde la página del proyecto, de lo contrario, puede encontrar el enlace en la parte inferior:

  • Los resultados del cálculo deben pasarse a un nuevo objeto, por lo que en lugar de (p1 + p2 + p3) debe hacer un nuevo punto (p1 + p2 + p3), (dado que su objeto definido por el usuario se llama " punto ").

  • Solo +, -, * y / son compatibles, el quinto operador aritmético% no lo es. La coerción de cadenas (" " + p1) y las comparaciones (p1 == p2) no funcionarán como se esperaba. Si es necesario, se deben crear nuevas funciones para estos fines, como (p1.val == p2.val).

  • Finalmente, los recursos computacionales necesarios para calcular la respuesta aumentan cuadráticamente con el número de términos. Por lo tanto, solo se permiten 6 términos en una cadena de cálculo por defecto (aunque esto se puede aumentar). Para cadenas de cálculo más largas que eso, divida los cálculos, como: nuevo punto (nuevo punto (p1 + p2 + p3 + p4 + p5 + p6) + nuevo punto (p7 + p8 + p9 + p10 + p11 + p12))

La página de Github .

Además de lo que ya se ha dicho: anular .valueOf () puede ayudar a producir una sobrecarga del operador bastante potente. En la prueba de concepto Fingers.js lib puede agregar oyentes de eventos en estilo .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;

La idea central es reemplazar temporalmente valueOf cuando se llama a ():

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

El número devuelto se puede deserializar nuevamente en función utilizando la matriz de controladores. Además, es posible extraer valores de bits del valor final (func1 + func2 - func3) de manera tan efectiva que pueda comprender qué funciones se agregaron y qué funciones se eliminaron.

Puede consultar la fuente en github y jugar con demo aquí .

Existe una explicación completa en este artículo (es para AS3, difícil ya que es ecmascript funcionará para JS).

Para algunos casos de uso limitado, puede hacer que el operador "sobrecargue" efectos:

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!

El código anterior es de uso gratuito bajo la licencia MIT. YMMV.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top