Перегрузка арифметических операторов в JavaScript?
Это лучший способ, я могу придумать формулировку этого вопроса, учитывая это определение класса JavaScript:
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;
};
};
и следующий тестовый установочный код:
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"
Есть ли способ неявного создания q4
в качестве объекта Quota
с использованием оператора сложения следующим образом...
var q4 = q1 + q2 + q3;
console.log("Quota 04 is " + q4.toString()); // Prints "Quota 04 is 86400000"
вместо того, чтобы прибегать к...
var q4 = new Quota(q1 + q2 + q3);
console.log("Quota 04 is " + q4.toString()); // Prints "Quota 04 is 24:00:00"
Если нет рекомендаций по лучшей практике в этой области для создания пользовательских числовых объектов JavaScript, скомпонованных через арифметические операторы?
Ответы
Ответ 1
Насколько мне известно, Javascript (по крайней мере, как он существует сейчас) не поддерживает перегрузку оператора.
Лучшее, что я могу предложить, это метод класса для создания новых объектов квот из нескольких других. Вот краткий пример того, что я имею в виду:
// 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)
}
и используйте его как:
var n1 = new NumClass(1);
var n2 = new NumClass(2);
var n3 = new NumClass(3);
var combined = NumClass.createFromObjects(n1, n2, n3);
Ответ 2
К сожалению нет.
Для резервных копий, если вы упорядочили возвращаемые значения, вы можете использовать цепочку методов
var q4 = q1.plus(p2).plus(q3);
Ответ 3
Поскольку все остальные проголосовали за мой другой ответ, я хотел опубликовать доказательство концептуального кода, который действительно работает по назначению.
Это было протестировано в chrome и 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;
Если кто-то будет так добр, чтобы дать техническое объяснение, ПОЧЕМУ это не достаточно хорошо, я был бы рад услышать это!
Ответ 4
Второе предложение:
var q4 = Quota.add(q1, q2, q3);
Ответ 5
Недавно я пришел к этой статье: http://www.2ality.com/2011/12/fake-operator-overloading.html.
В нем описывается, как вы можете переопределить метод valueOf на объектах, чтобы сделать что-то вроде перегрузки оператора в javascript. Похоже, вы можете реально выполнять операции с мутаторами на объектах, которые будут работать, поэтому он не будет делать то, что вы хотите. Интересно, тем не менее,
Ответ 6
Вы можете неявно преобразовывать ваши объекты в целочисленные или строковые.
Объекты неявно преобразуются только в том случае, если JavaScript ожидает число или строку. В первом случае преобразование выполняется в три этапа:
1.- Позвоните valueOf()
. Если результат является примитивным (не объектом), используйте его и преобразуйте в число.
2.- В противном случае позвоните toString()
. Если результат примитивный, используйте его и преобразуйте в число.
3.- В противном случае, бросьте TypeError
.
Пример для шага 1:
3 * { valueOf: function () { return 5 } }
Если JavaScript преобразуется в строку, шаги 1 и 2 меняются местами: сначала выполняется toString(), затем значение valueOf().
http://www.2ality.com/2013/04/quirk-implicit-conversion.html
Ответ 7
Paper.js делает это, например, с добавлением точки (docs):
var point = new Point(5, 10);
var result = point + 20;
console.log(result); // {x: 25, y: 30}
Но он делает это, используя собственный пользовательский script парсер.
Ответ 8
Я сделал script, который перегружает оператора в JavaScript. Однако было нелегко работать, поэтому есть несколько причуд. Я перейду с предупреждений здесь на странице проекта, иначе вы можете найти ссылку внизу:
-
Результаты вычислений должны быть переданы новому объекту, поэтому вместо (p1 + p2 + p3) вам нужно сделать новую точку (p1 + p2 + p3) (если ваш пользовательский объект назван "точкой" ").
-
Поддерживаются только +, -, * и /, пятый арифметический opperator% - нет.
Принуждение к строкам ( "+ p1" ) и сравнения (p1 == p2) не будут работать должным образом. При необходимости для этих целей должны быть созданы новые функции, например (p1.val == p2.val).
-
Наконец, вычислительные ресурсы, необходимые для вычисления ответа, квадратично возрастают с количеством членов. Поэтому допускается только 6 членов в одной цепочке вычислений по умолчанию (хотя это можно увеличить). Для более длинных вычислительных цепей, чем это, разделите вычисления вверх, например: новая точка (новая точка (p1 + p2 + p3 + p4 + p5 + p6) + новая точка (p7 + p8 + p9 + p10 + p11 + p12))
страница Github.
Ответ 9
Я не уверен, почему люди продолжают отвечать на этот вопрос без!
Существует абсолютно способ, которым я опишу очень маленький script, который вам не обязательно должен быть Джон Ресиг, чтобы понять...
Прежде чем я это сделаю, я также укажу, что в JavaScript способ, которым работал бы ваш конструктор, - проверка массивов или итерация литерала "arguments".
например. В моем конструкторе моего "класса" я бы перебирал меры, определял тип основных факторов и обрабатывал его разумно.
Это означает, что если вы передали массив, я бы итерации исправлений, чтобы найти массив, а затем повторить массив для дальнейшей обработки в зависимости от типа элемента в массиве.
например. → new someClass ([instanceA, instanceB, instanceC])
Однако вы, ребята, ищете более "C" подход к перегрузке оператора, который действительно может быть достигнут вопреки убеждению населения.
Вот класс, который я создал с помощью MooTools, который выполняет перезагрузку оператора. В обычном старом JavaScript вы просто используете тот же метод toString, чтобы привязать его непосредственно к прототипу экземпляра.
Моя основная причина для отображения этого подхода заключается в том, что текст, который я постоянно читаю, указывает, что эта функция "невозможно" подражать. Нет ничего невозможного, только достаточно сложно, и я покажу это ниже...
//////
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;
//////
Самое последнее отображение этой номенклатуры было отмечено на странице документации XDate:
http://arshaw.com/xdate/
В этом случае я считаю, что он был даже более легким, он мог использовать прототип объекта Date для достижения того же самого.
Тем не менее, метод, который я дал в качестве примера, который должен изображать этот стиль использования для других.
Edit:
У меня есть полная реализация здесь:
http://netjs.codeplex.com/
Наряду с другими лакомствами.
Ответ 10
В дополнение к уже сказанному: переопределение .valueOf() может помочь произвести довольно сильную перегрузку оператора. В доказательстве концепции Fingers.js lib вы можете добавить прослушиватели событий в стиле .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;
Основная идея состоит в том, чтобы временно заменить valueOf при вызове функции on():
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.
};
Возвращенный номер может быть затем де-сериализован обратно в функцию с помощью массива обработчиков. Что еще возможно, извлеките значения бит из окончательного значения (func1 + func2 - func3), чтобы вы могли понять, какие функции добавлены и какие функции были удалены.
Вы можете проверить источник на github и сыграть с demo здесь.
Полное объяснение существует в этой статье (она для AS3, жесткая, так как это ecmascript будет работать и для JS).
Ответ 11
На сегодняшний день вы можете переписать оператор '+', используя ExtendScript:
// define the constructor method
function MyClass (initialValue) {
this.value = initialValue;
}
// define the addition operator
MyClass.prototype ["+"] = function (operand) {
return this.value + operand;
}
var obj = new MyClass (5);
Result: [object Object]
obj + 10;
Result: 15
Престижность для примера: https://estk.aenhancers.com/8%20-%20ExtendScript%20Tools%20and%20Features/operator-overloading.html
Ответ 12
Для некоторых ограниченных вариантов использования вы можете иметь эффекты "перегрузки" оператора:
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!
Вышеприведенный код можно использовать в соответствии с лицензией MIT. YMMV.