代码之家  ›  专栏  ›  技术社区  ›  Peter McG

在javascript中重载算术运算符?

  •  63
  • Peter McG  · 技术社区  · 15 年前

    鉴于这个javascript“class”定义,这是我能想到的最好的表达这个问题的方法:

    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对象,该领域的最佳实践建议是什么?

    11 回复  |  直到 15 年前
        1
  •  33
  •   Dan Aditi    15 年前

    据我所知,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
  •  19
  •   Justin Love    15 年前

    不幸的是,没有。

    对于回退,如果排列返回值,则可以使用方法链接

    var q4 = q1.plus(p2).plus(q3);
    
        3
  •  13
  •   Jay    13 年前

    由于每个人都投了我的另一个答案,我想发布概念验证代码,它实际上按预期工作。

    这已经在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
  •  7
  •   Justin Love    15 年前

    第二条建议:

    var q4 = Quota.add(q1, q2, q3);
    
        5
  •  5
  •   B T    13 年前

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

    它描述了如何在对象上重新定义方法的值,以执行类似于javascript中的运算符重载之类的操作。似乎您只能对正在操作的对象执行mutator操作,所以它不会执行您想要的操作。尽管如此,它还是很有趣。

        6
  •  4
  •   Luis Naves    10 年前

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

    只有当javascript需要数字或字符串时,才会隐式转换对象。在前一种情况下,转换需要三个步骤:

    1.-调用valueof()。如果结果是基元(不是对象),则使用它并将其转换为数字。

    2.-否则,调用ToString()。如果结果是原语,请使用它并将其转换为数字。

    3.-否则,抛出一个类型错误。 步骤1的示例:

    3*valueof:函数()返回5

    如果javascript转换为字符串,则交换步骤1和2:首先尝试toString(),然后尝试valueof()。

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

        7
  •  4
  •   Izhaki    8 年前

    paper.js会这样做,例如添加点( docs ):

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

    但它用自己的 custom script parser .

        8
  •  2
  •   Jay    12 年前

    我不知道为什么人们会继续不回答这个问题!

    我绝对可以用一个非常小的剧本来概括,你不必非得是约翰·雷西格才能理解…

    在我这样做之前,我还将说明在JavaScript中,构造函数的工作方式是检查数组或迭代“arguments”文本。

    例如,在我的“类”的构造函数中,我将迭代这些规则,确定底层规则的类型,并智能地处理它。

    这意味着,如果您传递了一个数组,我将迭代这些数组以查找数组,然后迭代该数组以根据数组中元素的类型进行进一步的处理。

    例如->新建SomeClass([实例A、实例B、实例C])

    然而,你们正在寻找一种更“C”风格的方法来处理运算符重载,这种方法实际上可以实现,这与大众的看法相反。

    这里有一个类,我使用moooltools创建的,它支持操作符重载。在普通的旧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/

    在这种情况下,我认为这实际上更容易,他可以使用日期对象的原型来实现同样的效果。

    我举了一个例子来说明这种对他人的利用方式。

    编辑:

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

    http://netjs.codeplex.com/

    以及其他的好东西。

        9
  •  2
  •   Basic Block    8 年前

    我编写了一个脚本,用于在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 page .

        10
  •  1
  •   average Joe    11 年前

    除了已经说过的:overriding.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;
    

    核心思想是在调用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 和一起玩 demo here .

    此中存在完整的解释 article (这是针对AS3的,很难,因为它是ecmascript,也适用于JS)。

        11
  •  -1
  •   Eirik Birkeland    9 年前

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

    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!
    

    以上代码在麻省理工学院许可证下免费使用。YMMV。