在 JavaScript 中重载算术运算符?

发布于 2024-08-08 23:05:48 字数 1849 浏览 6 评论 0原文

这是我能想到的表达这个问题的最佳方式,考虑到这个 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< /code> 对象使用加法运算符,如下所示...

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 对象可组合的最佳实践建议是什么?

This is the best way I can think of phrasing this question, given this JavaScript "class" definition:

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

and the following test setup code:

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"

Is there any way of implicitly creating q4 as a Quota object using the addition operator as follows...

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

rather than resorting to...

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

If not what are the best practice recommendations in this area for making custom numeric JavaScript objects composable via the arithmetic operators?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

落墨 2024-08-15 23:05:48

据我所知,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);

As far as I'm aware, Javascript (at least as it exists now) doesn't support operator overloading.

The best I can suggest is a class method for making new quota objects from several others. Here's a quick example of what I mean:

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

and use it like:

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

var combined = NumClass.createFromObjects(n1, n2, n3);
手心的海 2024-08-15 23:05:48

不幸的是没有。

对于后备,如果您安排了返回值,则可以使用方法链接

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

Unfortunately no.

For fallbacks, if you arranged the return values, you could use method chaining

var q4 = q1.plus(p2).plus(q3);
会发光的星星闪亮亮i 2024-08-15 23:05:48

由于每个人都否决了我的其他答案,我想发布概念验证代码,该代码实际上按预期工作。

这已经在 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;

如果有人愿意给出技术解释为什么这还不够好,我会很高兴听到它!

Since everyone down voted my other answer I wanted to post proof of concept code which does in fact work as intended.

This has been tested in chrome and 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;

If someone would be so kind as to give a technical explanation WHY this is not good enough I would be happy to hear it out!

饮湿 2024-08-15 23:05:48

Paper.js 做到了这一点,例如通过点添加(docs):

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

但它使用它自己的自定义脚本解析器

Paper.js does it, for example with point addition (docs):

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

But it does it using its own custom script parser.

巷雨优美回忆 2024-08-15 23:05:48

您可以将对象隐式转换为整数或字符串。

仅当 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-隐式转换.html

You can implicitly convert to integer or string, your objects.

Objects are only implicitly converted if JavaScript expects a number or a string. In the former case, the conversion takes three steps:

1.- Call valueOf(). If the result is primitive (not an object) then use it and convert it to a number.

2.- Otherwise, call toString(). If the result is primitive, use it and convert it to a number.

3.- Otherwise, throw a TypeError.
Example for step 1:

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

If JavaScript converts to string, steps 1 and 2 are swapped: toString() is tried first, valueOf() second.

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

月竹挽风 2024-08-15 23:05:48

第二个建议:

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

Second suggestion:

var q4 = Quota.add(q1, q2, q3);
琉璃繁缕 2024-08-15 23:05:48

我最近看到这篇文章: http://www.2ality.com/ 2011/12/fake-operator-overloading.html

它描述了如何重新定义对象的 valueOf 方法来执行类似于 JavaScript 中的运算符重载之类的操作。似乎您只能对正在操作的对象真正执行变异操作,因此它不会执行您想要的操作。尽管如此,它还是很有趣。

I recently came upon this article: http://www.2ality.com/2011/12/fake-operator-overloading.html .

It describes how you can redefine the valueOf method on objects to do something like operator overloading in javascript. It seems like you can only really perform mutator operations on the objects being operated on, so it wouldn't do what you want. Its interesting nonetheless tho.

怪我太投入 2024-08-15 23:05:48

我制作了一个在 JavaScript 中执行运算符重载的脚本。创作起来并不容易,所以还是有一些怪癖。我将从项目页面交叉发布警告,否则您可以在底部找到链接:

  • 计算结果必须传递给新对象,因此您必须执行 new 而不是 (p1 + p2 + p3) point(p1 + p2 + p3),(假定您的用户定义对象名为“point”)。

  • 仅支持 +、-、* 和 /,不支持第五个算术运算符 %。
    字符串强制 (""+p1) 和比较 (p1 == p2) 将无法按预期工作。如果需要,应为此目的构建新函数,例如 (p1.val == p2.val)。

  • 最后,计算答案所需的计算资源随着项数呈二次方增加。因此,每个默认值在一个计算链中只允许有 6 个项(尽管可以增加)。对于比这更长的计算链,请将计算分开,例如: new point(new point(p1 + p2 + p3 + p4 + p5 + p6) + new point(p7 + p8 + p9 + p10 + p11 + p12))< /p>

) Github 页面

I made a script that does operator overloading in JavaScript. It wasn't straight forward to make work, so there are a few quirks though. I will cross post the caveats here from the project page, otherwise you can find the link at the bottom:

  • Calculation results must be passed to a new object, so instead of (p1 + p2 + p3) you have to do new point(p1 + p2 + p3), (given your user defined object is named "point").

  • Only +, -, * and / are supported, the fifth arithmetic opperator % is not.
    Coercion to strings (""+p1) and comparisons (p1 == p2) will not work as expected. New functions should be built for these purposes if needed, like (p1.val == p2.val).

  • Finally the computational resources needed to calculate the answer increases quadratically with the number of terms. Therefore only 6 terms is allowed in one calculation chain per default (although this can be increased). For longer calculation chains than that, split the calculations up, like: new point(new point(p1 + p2 + p3 + p4 + p5 + p6) + new point(p7 + p8 + p9 + p10 + p11 + p12))

The Github page.

单身情人 2024-08-15 23:05:48

除了已经说过的之外:重写 .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;

Core想法是在调用 on() 时临时替换 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 上查看源代码并使用 此处演示

完整的解释存在于这篇文章< /a> (它适用于 AS3,因为它是 ecmascript,所以它也适用于 JS)。

In addition to what already have been said: overriding .valueOf() may help to produce quite powerful operator overloading. In proof-of-concept Fingers.js lib you can add event listeners in .NET style:

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;

Core idea is to replace temporarily valueOf when on() is called:

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

Number returned can be then de-serialized back into function using handlers array. What's more it's possible extract bit values from final value (func1 + func2 - func3) so effectively you can understand what functions where added, and what functions were removed.

You can check out source on github and play with demo here.

Complete explanation exists in this article (it's for AS3, tough since it's ecmascript it will work for JS either).

水中月 2024-08-15 23:05:48

我不知道为什么人们继续以“不”来回答这个问题!

绝对有一种方法,我将用一个非常非常小的脚本概述,您不必是 John Resig 也能理解......

在我这样做之前,我还要声明,在 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 对象的原型来实现相同的效果。

尽管如此,我作为示例给出的方法应该向其他人展示这种使用方式。

编辑:

我在这里有一个完整的实现:

http://netjs.codeplex.com/

以及其他好东西。

I am not sure why people continue to answer this question with no!

There is absolutely a way which I will outline with a very very small script which your don't have to be John Resig to understand...

Before I do so I will also state that in JavaScript the way your constructor would have worked is by checking for arrays or iterating the 'arguments' literal.

e.g. In my constructor of my 'class' I would iterate the arugments, determine the type of the underlying arugments and process it intelligently.

This means that if you passed an array I would iterate the arugments to find an array and then iterate the array to do further processing depending on the type the element in the array.

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

However you guys are seeking a more "C" style approach to operator overloading which can actually be achived contrary to populare belief.

Here is a class which I have created using MooTools which does honor operator overloading. In plain old JavaScript you would just utilize the same toString method only attach it to the prototype of instance directly.

My main reason for displaying this approach is because of the text I continually read which states this functionality is "impossible" to emulate. Nothing is impossible only sufficently difficult and I will display this below...

 //////

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;

//////

The most recent display of this nomenclature was observed at XDate's documentation page:
http://arshaw.com/xdate/

In this case I believe it was actually even easer, he could have used the prototype of the Date object to achive the same.

None the less the method I have given as an example which should portray this style of utilization for others.

Edit:

I have a complete implementation here:

http://netjs.codeplex.com/

Along with other goodies.

剩一世无双 2024-08-15 23:05:48

对于某些有限的用例,您可以具有运算符“重载”效果:

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。

For some limited use cases you can have operator "overloading" effects:

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!

The above code is free to use under the MIT license. YMMV.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文