2009-10-27 2 views
58

Это самый лучший способ, которым я могу думать формулируя этот вопрос, учитывая это 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" 

Rathe г не прибегать к ...

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

Если не то, что лучшие практические рекомендации в этой области для создания пользовательского числового JavaScript объектов компонуемы с помощью арифметических операторов?

+0

Посмотрите на [SweetJS] (http://sweetjs.org/), который может сделать именно это. –

+0

Здесь: https: // stackoverflow.com/q/10539938/632951 – Pacerier

ответ

32

Насколько я знаю, 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); 
+0

http://www.2ality.com/2011/12/fake-operator-overloading.html лучше. – Pacerier

+0

Я немного удивлен тем, что язык, возвращающий отрицательные значения от отрицательных операндов к его модулю, не будет поддерживать перегрузку оператора. Каждый в мире на этом этапе должен реализовывать% as ((a% b) + b)% b –

18

К сожалению, нет.

Для откаты, если вы расположил возвращаемые значения, можно использовать метод формирования цепочки

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

Если ваша среда поддерживает его, вы также можете использовать currying для более симпатичного API: 'one (two) (three)' – ELLIOTTCABLE

+4

@elliottcable Хорошее + умное мышление, но пока это может быть хорошо для умножения, даже тогда я не думаю, что он хорошо общается в типичном мышлении программиста. Я по-прежнему буду использовать 'one.times (two) .times (three);'. – AndrewK

+0

В CoffeeScript вы можете отбросить и некоторые из них: –

7

Второе предложение:

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

Я не знаю, почему люди продолжают отвечать на этот вопрос нет!

Существует абсолютно путь, который я перечислю с очень очень маленьким скриптом, который ваш не должен быть Резиг понять ...

Прежде чем я, поэтому я также, что в JavaScript в каким образом ваш конструктор работал бы, это проверка массивов или повторение литерала «arguments».

например. В моем конструкторе моего «класса» я повторял бы исправления, определял тип основных сокращений и обрабатывал его разумно.

Это означает, что если вы передали массив, я бы итерации исправлений, чтобы найти массив, а затем итерации массива для дальнейшей обработки в зависимости от типа элемента в массиве.

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

Однако вы, ребята, ищете более «C» подход к перегрузке оператора, который действительно может быть достигнут вопреки убеждению населения.

Вот класс, который я создал с помощью MooTools, который выполняет перезагрузку оператора.В обычном старом JavaScript вы просто используете тот же метод toString, чтобы напрямую присоединить его к прототипу экземпляра.

Основная причина для отображения этого подхода заключается в том, что текст, который я постоянно читаю, утверждает, что эта функция «невозможно» подражать. Нет ничего невозможного только sufficently трудно, и я буду показывать это ниже ...

////// 

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/

Наряду с другими вкусностями.

+3

Что вы здесь показали, это не перегрузка оператора. Кроме того, в вашем примере 'toString()' возвращает ''0'' в' stringTest'. Вы должны использовать 'valueOf()' для возврата подстановки номера (более того, здесь: http://www.adequatelygood.com/2010/3/Object-to-Primitive-Conversions-in-JavaScript). Но это всего лишь замена значения, это не перегрузка функций оператора. Даже с вашим методом я не могу реализовать класс 'Vector', который бы вычитал поля' .x' и '.y', когда мне это нравится:' vectorC = vectorA - vectorB'. Для этого вам требуется перегрузка оператора, что невозможно в ES5. – pepkin88

+0

Я обнаружил, что используя комбинацию вышеуказанной тактики с функцией.bind, вы можете контролировать, какая версия метода вызывается, и она работает, хотя она не такая гибкая, как на других языках ... Например. Вы можете сделать функцию .cast, которая принимает объект и вызывает метод другого типа с контекстом, заданным для данного объекта. Тем не менее это не то же самое, что и на других языках, но я все же утверждаю, что он может работать: P – Jay

+0

@Jay Я попробовал это в своем браузере и в Node.js, и он жалуется, что класс не определен, поэтому он не работает ......... О, подождите, я вижу: Класс из MooTools. Можете ли вы выполнить эту работу с помощью jQuery? Или даже лучше, без какой-либо библиотеки вообще, просто JavaScript? – trusktr

5

Я недавно наткнулся на эту статью: http://www.2ality.com/2011/12/fake-operator-overloading.html.

В нем описывается, как вы можете переопределить метод valueOf для объектов, чтобы сделать что-то вроде перегрузки оператора в javascript. Похоже, вы можете реально выполнять операции с мутаторами на объектах, которые будут работать, поэтому он не будет делать то, что вы хотите. Его интересный, тем не менее,

12

Поскольку все остальные проголосовали за мой другой ответ, я хотел опубликовать доказательство концептуального кода, который на самом деле работает по назначению.

Это было протестировано в хроме и 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; 

Если кто-то будет так любезны дать техническое объяснение, почему это не достаточно хорошо, я был бы рад услышать это!

+1

Передача этого нового типа в литье может быть переопределена, если требуется от производного .... – Jay

+0

Я добавил это в свою библиотеку @ http://netjs.codeplex.com/documentation – Jay

+4

Джей, вы можете показать, как это будет работать с класс MyNumber для выполнения арифметики (пример)? – trusktr

-1

Для некоторых ограниченных случаев использования вы можете иметь оператор «перегрузки» эффекты:

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.

+5

Бесплатно для использования в рамках лицензии MIT? Я не думаю, что вы понимаете, что это за сайт. –

+1

@AranMulholland У вас? Текущая лицензия SE - CC BY-SA (всегда была), и они планируют перейти на какой-то MIT http://meta.stackexchange.com/questions/272956/a-new-code-license-the-mit требуемое время-с-атрибуция –

+0

Если я правильно понимаю, это не перегружает оператора, а просто передает его первоначальную реализацию. Так что очень ограниченное использование, как вы указываете. –

0

В дополнение к уже сказанному: переопределение .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, когда на() называется:

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 here.

Полное объяснение существует в этом article (это для AS3, жесткий, так как это ecmascript, он будет работать и для JS).

4

вы можете неявно преобразовать в целое число или строка, ваши объекты

объекты только неявно преобразованы, если JavaScript ожидает число или строку. В первом случае преобразование выполняется в три этапа:

1.- Вызвать значениеOf(). Если результат примитивен (а не объект), используйте его и преобразуйте в число.

2.- В противном случае вызовите toString(). Если результат примитивен, используйте его и преобразуйте в число.

3.- В противном случае бросьте TypeError. Пример для стадии 1:

3 * {valueOf: функция() {вернуть 5}}

Если JavaScript преобразует в строку, шаги 1 и 2 меняются местами: ToString() сначала выполняется попытка , valueOf() second.

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

0

Paper.js это делает, например, с точки добавления (docs):

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

Но он делает это с помощью своих собственных custom script parser.

1

Я сделал сценарий, который перегружает оператора в 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 page.

Смежные вопросы