Перегрузка арифметических операторов в JavaScript?

StackOverflow https://stackoverflow.com/questions/1634341

  •  06-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

Насколько мне известно, 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);

Другие советы

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

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

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

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

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

Если кто-нибудь будет так любезен дать техническое объяснение, ПОЧЕМУ этого недостаточно, я был бы рад его выслушать!

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

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

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

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

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

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

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

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

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

3 * { Значение:функция () { возврат 5 } }

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

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

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

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

Но он делает это, используя свой собственный анализатор пользовательских скриптов.

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

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

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

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

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

Например.-> создать 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 для достижения того же самого.

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

Редактировать:

У меня здесь есть полная реализация:

http://netjs.codeplex.com/

Вместе с другими вкусностями.

Я создал скрипт, который выполняет перегрузку оператора в JavaScript.Это было непросто сделать прямолинейно, так что, однако, есть несколько причуд.Я опубликую предостережения здесь, на странице проекта, в противном случае вы можете найти ссылку внизу:

  • Результаты вычисления должны быть переданы новому объекту, поэтому вместо (p1 + p2 + p3) вы должны создать новую точку (p1 + p2 + p3), (учитывая, что ваш пользовательский объект называется "точка").

  • Поддерживаются только +, -, * и /, пятый арифметический параметр % - нет.Принуждение к строкам (""+ p1) и сравнения (p1 == p2) не будут работать должным образом.При необходимости для этих целей следует создать новые функции, например (p1.val == p2.val).

  • Наконец, вычислительные ресурсы, необходимые для вычисления ответа, увеличиваются квадратично с увеличением количества терминов.Следовательно, по умолчанию в одной цепочке вычислений допускается только 6 терминов (хотя это количество может быть увеличено).Для более длинных цепочек вычислений, чем эта, разделите вычисления следующим образом:новая точка (новая точка (p1 + p2 + p3 + p4 + p5 + p6) + новая точка (p7 + p8 + p9 + p10 + p11 + p12))

Тот самый Страница на Github.

В дополнение к тому, что уже было сказано:переопределение .valueOf() может помочь создать довольно мощную перегрузку оператора.В доказательстве концепции Fingers.js библиотека вы можете добавить прослушиватели событий в стиле .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), чтобы вы могли эффективно понимать, какие функции были добавлены, а какие были удалены.

Вы можете ознакомиться с источником на гитхаб и играть с демо-версия здесь.

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

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

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.ИММВ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top