Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fake operator overloading in JavaScript

Fake operator overloading in JavaScript

Axel Rauschmayer

May 31, 2012
Tweet

More Decks by Axel Rauschmayer

Other Decks in Programming

Transcript

  1. [intro] conversion plus calls strbuilder point wat end What to

    expect 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); Credit: Tobias Schneider (def.js) Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 2 / 37
  2. [intro] conversion plus calls strbuilder point wat end Overview Converting

    to primitive The plus operator (+) Triggering function/method calls via + Implementing StringBuilder Implementing Point WAT Conclusion Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 3 / 37
  3. [intro] conversion plus calls strbuilder point wat end Primitives versus

    objects The following values are primitives: Booleans Numbers Strings undefined null All other values are objects. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 4 / 37
  4. [intro] conversion plus calls strbuilder point wat end Applying operators

    to objects JavaScript operators always work with primitive values. obj1 op obj2 Convert obj1 and obj2 to primitives. Compute the result. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 5 / 37
  5. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  6. intro [conversion] plus calls strbuilder point wat end ToPrimitive(): converting

    values to primitives ToPrimitive(value, PreferredType?) PreferredType is Number: If value is primitive, return it as is. Otherwise, value is object. Call value.valueOf(). If primitive, return. Call value.toString(). If primitive, return. Otherwise, throw a TypeError. PreferredType is String: swap steps 2 and 3. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 7 / 37
  7. intro [conversion] plus calls strbuilder point wat end ToNumber(): converting

    values to numbers Convert primitive: Argument Result undefined NaN null +0 boolean value true is converted to 1, false is converted to +0 number value no conversion necessary string value parse number ("324" becomes 324) Convert object: call ToPrimitive(obj, Number), convert result to number. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 8 / 37
  8. intro [conversion] plus calls strbuilder point wat end ToString(): converting

    values to strings Convert primitive: Argument Result undefined "undefined" null "null" boolean value either "true" or "false" number value the number as a string, e.g. "1.765" string value no conversion necessary Convert object: call ToPrimitive(obj, String), convert result to string. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 9 / 37
  9. intro [conversion] plus calls strbuilder point wat end Trying out

    conversion var obj = { valueOf: function () { console.log("valueOf"); return {}; // not primitive }, toString: function () { console.log("toString"); return {}; // not primitive } } Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 10 / 37
  10. intro [conversion] plus calls strbuilder point wat end Trying out

    conversion: interaction > Number(obj) valueOf toString TypeError: Cannot convert object to primitive value > String(obj) toString valueOf TypeError: Cannot convert object to primitive value Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 11 / 37
  11. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  12. intro conversion [plus] calls strbuilder point wat end The plus

    operator (+) value1 + value2 Evaluation: Convert value1 and value2 via ToPrimitive(Number). Either one is string: convert both to string, concatenate. Otherwise: convert both to number, add. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 13 / 37
  13. intro conversion [plus] calls strbuilder point wat end Trying out

    + var obj = { valueOf: function () { console.log("valueOf"); return {}; // not primitive }, toString: function () { console.log("toString"); return 123; // primitive } } Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 14 / 37
  14. intro conversion [plus] calls strbuilder point wat end Trying out

    +: interaction > 3 + obj valueOf toString 126 > "3" + obj valueOf toString '3123' Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 15 / 37
  15. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  16. intro conversion plus [calls] strbuilder point wat end How many

    function/method calls? x = f(1) + f(2) Answer: 7 Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 17 / 37
  17. intro conversion plus [calls] strbuilder point wat end The operands:

    6 calls function f(pos) { console.log("f"+pos); return { valueOf: function () { console.log("valueOf"+pos); return {}; // not primitive }, toString: function () { console.log("toString"+pos); return "result"; // primitive } }; } Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 18 / 37
  18. intro conversion plus [calls] strbuilder point wat end One more

    call x = f(1) + f(2) Define a setter for property x of global object. Object.defineProperty(this, "x", { set: function (x) { console.log("set: "+x); } }); Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 19 / 37
  19. intro conversion plus [calls] strbuilder point wat end Interaction >

    x = f(1) + f(2) f1 f2 valueOf1 toString1 valueOf2 toString2 set: resultresult 'resultresult' Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 20 / 37
  20. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  21. intro conversion plus [calls] strbuilder point wat end Usage var

    sb = new StringBuilder(); sb << add("abc") << add("def"); console.log(sb.toString()); // abcdef Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 22 / 37
  22. intro conversion plus [calls] strbuilder point wat end sb <<

    function StringBuilder() { this.data = ""; } // called by << StringBuilder.prototype.valueOf = function () { StringBuilder.current = this; }; Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 23 / 37
  23. intro conversion plus [calls] strbuilder point wat end add("abc") <<

    add("abc") function add(value) { return { valueOf: function () { StringBuilder.current.data += value; } } } Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 24 / 37
  24. intro conversion plus [calls] strbuilder point wat end Return the

    built string StringBuilder.prototype.toString = function () { return this.data; }; Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 25 / 37
  25. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  26. intro conversion plus calls strbuilder [point] wat end Usage 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) Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 27 / 37
  27. intro conversion plus calls strbuilder [point] wat end Being an

    operand of +, *, etc. Point.prototype.valueOf = function () { Point.operands.push(this); return 3; } Lowest natural number x where the following are all different: x + x x − x x · x x / x Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 28 / 37
  28. intro conversion plus calls strbuilder [point] wat end // p._

    = ... Object.defineProperty(Point.prototype, "_", { set: function (value) { var ops = Point.operands; var operator; if (ops.length >= 2 && (value === 3 * ops.length)) { // 3 + 3 + ... operator = this.setAdd; } else if (ops.length === 2 && value === 0) { // 3 - 3 operator = this.setSubtract; } else ... Point.operands = []; // reset return operator.apply(this, ops); } }); Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 29 / 37
  29. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  30. intro conversion plus calls strbuilder point [wat] end WAT? (1/2)

    > [] + [] '' Explanation: > [].valueOf() [] > [].toString() '' Makes sense... Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 31 / 37
  31. intro conversion plus calls strbuilder point [wat] end WAT? (2/2)

    > {} + {} NaN Two components: Block {} Expression +{} (object literal) Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 32 / 37
  32. intro conversion plus calls strbuilder point [wat] end Computing +{}

    The following expressions are all equivalent: +{} Number({}) Number({}.toString()) // {}.valueOf() isn't primitive Number("[object Object]") NaN Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 33 / 37
  33. intro conversion plus calls strbuilder point [wat] end Properly adding

    objects > ({} + {}) '[object Object][object Object]' Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 34 / 37
  34. Converting to primitive The plus operator (+) Triggering function/method calls

    via + Implementing StringBuilder Implementing Point WAT Conclusion
  35. intro conversion plus calls strbuilder point wat [end] Conclusion Operators

    always work with primitive values Fake operator overloading: fun hack, not much more Instead, use method names (see Java): plus, minus, etc. Axel Rauschmayer (rauschma.de) Fake operator overloading 2012-05-31 36 / 37