Update 2012-01-29: The post “What is {} + {} in JavaScript?” looks at the addition operator in more detail.

This post describes how to do a limited version of operator overloading in JavaScript. With the technique described here, you’ll be able to implement a type StringBuilder that can be used as follows:

var sb = new StringBuilder(); sb << add("abc") << add("def");

Point

var p = new Point(); p._ = new Point(1, 2) + new Point(3, 4) + new Point(5, 6); p._ = new Point(1, 2) * new Point(3, 4) * new Point(5, 6);

Letting operators call methods

obj1 + obj2

obj1.operator+(obj2)

obj1.valueOf() obj2.valueOf()

+

obj1

obj2

valueOf()

+

And a typethat can be used as follows:One version of real operator overloading works like this: Given the expressionThe plus operator is applied to two objects. This triggers the method callThe closest thing you can achieve in JavaScript is– triggering two method calls:Those are made becauseonly works with primitive values and thus needs to convertandto primitives. It does so by invoking theirmethod. Fake operator overloading is much less useful than real operator overloading: You don’t get access to both operands at the same time and you can’t influence the value returned by. We’ll later look at tricks that work around these limitations.

What operators can be used for fake operator overloading? All operators that coerce (convert) their operands to primitives. The following two objects allow you to test which ones do:

var obj1 = { valueOf: function () { console.log("valueOf1"); return 1; } }; var obj2 = { valueOf: function () { console.log("valueOf2"); return 2; } };

> obj1 + obj2 valueOf1 valueOf2 3

+ - * / % & | ^ << >> >>> < <= > >=

> obj1 === obj2 false > obj1 && obj2 { valueOf: [Function] }

function func(x) { console.log("func_"+x); return { valueOf: function() { console.log("valueOf_"+x) } }; }

<<

> func("LEFT") << func("RIGHT") func_LEFT func_RIGHT valueOf_LEFT valueOf_RIGHT

valueOf_LEFT

func_LEFT

> func("LEFT") > func("RIGHT") func_LEFT func_RIGHT valueOf_RIGHT valueOf_LEFT

Implementing StringBuilder

StringBuilder

var sb = new StringBuilder(); sb << add("abc") << add("def"); console.log(sb.toString()); // abcdef

function StringBuilder() { this.data = ""; } // Called by << StringBuilder.prototype.valueOf = function () { StringBuilder.current = this; }; // Used to access the aggregated string StringBuilder.prototype.toString = function () { return this.data; }; function add(value) { return { valueOf: function () { StringBuilder.current.data += value; } } }

<<

StringBuilder.prototype.valueOf

StringBuilder.current

add()

value

value

def.js – fake operator overloading used for an inheritance API

def ("Person") ({ init: function(name){ this.name = name; }, speak: function(text){ alert(text || "Hi, my name is " + this.name); } }); def ("Ninja") << Person ({ init: function(name){ this._super(); }, kick: function(){ this.speak("I kick u!"); } }); var ninjy = new Ninja("JDD"); ninjy.speak(); ninjy.kick();

(I) def ("Person") ({...}) (II) def ("Ninja") << Person ({...})

def()

Person

As a constructor: Then it simply produces a new instance.

As a function: Then it must return an object that works as a fake operand.

Perform the function call def("Ninja") which creates a new constructor Ninja and returns a function D . D could be called, like in (I). In which case it would add properties to Ninja.prototype . But here it is used as a fake operand – def() has added the method D.valueOf() which will be invoked in step 3. D is also stored in a global variable (hidden inside a closure). Person ({...}) is invoked as a function and stores two values inside D : D.props holds Person ’s argument

holds ’s argument D.super refers to Person The operator calls D.valueOf() which first lets Ninja (which is still accessible via D ’s closure) inherit from D.super and then calls D to add the properties stored in D.props .

Triggering even more calls

func()

func("LEFT") << func("RIGHT")

func()

function func(x) { console.log("func_" + x); return { valueOf: function () { console.log("valueOf_"+x); return {}; // not a primitive }, toString: function () { console.log("toString_"+x); return 0; // a primitive }, } }

func()

> func("LEFT") << func("RIGHT") func_LEFT func_RIGHT valueOf_LEFT toString_LEFT valueOf_RIGHT toString_RIGHT

<<

ToNumber()

ToNumber()

valueOf()

toString()

toString()

TypeError

ToNumber()

valueOf()

toString()

toString()

Detecting the operator

var p = new Point(); p._ = new Point(1, 2) + new Point(3, 4) + new Point(5, 6); console.log(p.toString()); // Point(9, 12) p._ = new Point(1, 2) * new Point(3, 4) * new Point(5, 6); console.log(p.toString()); // Point(15, 48)

valueOf()

Point.prototype.valueOf = function () { Point.operands.push(this); return 3; }

3

x

x + x x - x x * x x / x

p._

Point.operands

Object.defineProperty(Point.prototype, "_", { set: function (value) { var ops = Point.operands; var operator; if (ops.length === 2 && value === 0) { // 3 - 3 operator = this.setSubtract; } else if (ops.length === 2 && value === 1) { // 3 / 3 operator = this.setDivide; } else if (ops.length >= 2 && (value === 3 * ops.length)) { // 3 + 3 + 3 + ... operator = this.setAdd; } else if (ops.length >= 2 && (value === Math.pow(3, ops.length))) { // 3 * 3 * 3 * ... operator = this.setMultiply; } else { throw new Error("Unsupported operation (code "+value+")"); } Point.operands = []; // reset return operator.apply(this, ops); } });

ops.length

value

Addition: value === 3 * ops.length

Multiplication: value === 3ops.length

How useful is fake operator overloading?

valueOf()

StringBuilder

add()

Related reading

For example:The following binary operators coerce:Equality operators, inequality operators, and boolean operators can work with objects and thus don't coerce. For example:If its operands are produced by function calls, then a binary operator triggers a total of four function (or method) calls: First, it evaluates the two operand expressions to values (in this case, objects). Then, it converts two objects to primitives. The following function allows us to examine what happens and in which order:Let’s use it to examine theoperator:Hence: First, the left operand is evaluated, then the right operand. Next, the left value is converted to a primitive, then the right value. It is slightly perplexing to see thatdoes not happen directly after, but it makes sense if you think of function calls, where one also first evaluates the parameters before executing the function body. Some operators even convert their right operands first:Recap: We wantto behave as follows:Fake operator overloading allows us to do this:Explanation: Theoperator callswhich marks the current instance as the “receiver” of subsequent “messages” (by storing it in). The messages are sent via, which wraps its argumentin an object. When that object is contacted by the operator, it addsto the current receiver.Tobias Schneider’s def.js is where I first saw the idea of fake operator overloading. def.js uses it to implement a small domain-specific language, giving you a rubyesque syntax for inheritance in JavaScript. Example:There are two ways to use the API:Therefore, whatever is returned bymust be both callable (i.e., a function) and a potential fake operand. Furthermore, there are two ways of invoking(which has been created by def.js):Expression (II) is evaluated in three steps (simplified for the purpose of this explanation):above allowed us to get four function (and method) calls out of the following expression:Can we rewriteto get even more calls? Yes, we can:The above implementation ofgives us six calls:The trick here is thatinternally performs a[1] conversion.first tries. If that method does not return a primitive value, it continues with. Ifdoes not return a primitive, either, then ais thrown. Note thatonly expects the result of eitherorto be a primitive which it then converts to a number. Hence,returning a string is only enforced by convention in JavaScript.If you combine fake operator overloading with a setter, you can detect which operator is used [credit: inspired by an idea of Tobias Schneider’s]:This works as follows. Each of the operands of the plus operator is converted viaThis method stores the operand we actually want to use away in a global variable. It then returns(a value that the plus operator can work with). That number is the lowest natural numberfor which all of the following expressions produce different results:has a setter that receives the result of 3 being added, multiplied etc., figures out which operator was used and processesaccordingly.For addition and multiplication, the numberof operands can be greater than 2. We therefore test the resultof the operator application as follows:The full source code of Point is available on GitHub.While fake operator overloading is a fun hack, you probably shouldn’t use it in production code: you cannot freely use the result computed by an operator and need side effects (such as storing values in global variables) to access all operands at the same time. Furthermore, many operands need to be wrapped in an object with amethod in order for this scheme to work. For example, we couldn’t use strings directly with, we had to convert them via. But at the very least, fake operator overloading allows you to impress your friends by unusual-looking code.