Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member
0 Kudos

Foreword


I hope all you know how common arithmetic and logical operators works in JavaScript. Even if you are using other language you may make a guess, and this guess will be correct. However, JavaScript dynamic type system and implicit type conversions allows you to use well-known operators in very creative way. Ok, if not creative, then at least in funny way. :wink: Today we pay attention to simple arithmetic operators</p>

Multiplication / Division


So let us start with simple example



var a = 256;
var b = 256 * 1;

 


Obviously, that b is 256 now. Now consider the following example:



var a = "128";
var b = a * 1;

 


Can you guess a result? Error, NaN or...?


Well, the result is 128 (no quotes, it's a number). So you have cheap replacement for parseInt(str, 10). However, it is not a complete replacement:



var a1 = "256";
var b1_1 = a1 * 1;
var b1_2 = parseInt(a1, 10);
var a2 = "128abc";
var b2_1 = a2 * 1;
var b2_2 = parseInt(a2, 10);

 


Here both b1_1 and b1_2 equals to 256. Still b2_1 is NaN but b2_2 is 128.



Even more surprises:



var a1; /* value is undefined */
var a2 = null;
var b1 = a1 * 1;
var b2 = a2 * 1;

 


Here b1 is NaN (Not A Number, never says "equals to NaN", NaN not equals event to itself ;). But b2 here is 0 (zero).


And what about other types?



var a1 = true;
var a2 = false;
var b1 = a1 * 1;
var b2 = a2 * 1;

 


I hope you are prepared this time. b1 equals to 1, b2 equals to 0 (zero). This fact can be very useful if you must convert Boolean to Number to pass it further to ActiveX or XPCOM interface that explicitly mandates parameter of type Number. Or if you want to pass parameter to some Java class method from Rhino.



Be creative! Combine different types!



var str = "123";
var val1 = str * true;
var val2 = str * false;

 


Wondering whether or not it could be ever evaluated? Try it! val1 === 123, val2 === 0.



And just to complement "Multiplication" part of article:



var currentDateTime = new Date;
var currentDateTimeInMillisSince1970 = currentDateTime * 1;

 


No comments. Actually, thorough variable naming save you from commenting code 😉



Just a final hint: Object and Function multiplied by 1 gives NaN. We will consider Array at the end of article. Custom objects? Let us create one:



function MyCustomObject() {}
MyCustomObject.prototype.valueOf = function() { return 4567; }
var value = new MyCustomObject * 1;

 


What??? value === 4567! (exclamations in this post are not factorials). Nice trick, heh? By the way, this is why multiplication with Date object works. Wanna more?



function YetAnotherCustomObject() {}
YetAnotherCustomObject.prototype.toString = function() { return "7654"; }
var value = new YetAnotherCustomObject * 1;

 


Are you shocked? Not? Ok, this is not closing yet... But enough multiplication for today. Next time we will multiply by 2 :wink: ))</p>


Addition / Subtraction


Ok, now you trained enough to understand the following:



var value;
var str = "12";
value = str - 1; // value now 11;
var bool
bool = true;  value =  bool - 1; /* 0, zero /; value = bool + 1; / 2 */
bool = false; value =  bool - 1; /* -1      /; value = bool + 1; / 1 */
value = str - true;  /* 11 */
value = str - false; /* 12 */

 


Pay attention, though, that +str + 1 === "121"+, and it is string. So correct formual (if we combine our deep multiplication knowledge :wink: is +str * 1 + 1 === 13+. Same thing, +str + true === "12true"+, so use +str * 1 + true === 13+.



Now some things that may cause real problems in your scripts:



var value;
value = null + 1; /* value equals to 1 */
value = null - 1; /* value equals to -1 */

 



Imaging that null is not constants, but some variable that you set to null and then forget to reassign... So left null values to object references, do not initialize other variables this way, instead let undefined type work for you and produce NaN in any operation. At least, you will find pesky bugs immediately.



If we turn back to our previous MyCustomObject and YetAnotherCustomObject we get the following results:



var value;
value = new MyCustomObject + 1; /* 4568 */
value = new MyCustomObject - 1; /* 4566 */
value = new YetAnotherCustomObject + 1; /* "76541" */
value = new YetAnotherCustomObject - 1; /* 7653 */

 



Again, notice that in case of addition with YetAnotherCustomObject we get concatenated string. Let us alter our MyCustomObject definition:



function MyCustomObject() {
}
MyCustomObject.prototype.toString = function() { return "7654"; }
MyCustomObject.prototype.valueOf = function() { return 4567; }

value = new MyCustomObject + 1; /* 4568 */
value = new MyCustomObject - 1; /* 4566 */

 



Obviously, valueOf() definition wins. Well, and what happens if we change prototypes chain?



function MyObject1() {
}
MyObject1.prototype.valueOf = function() { return 4567; }

function MyObject2() {
}

MyObject2.prototype = new MyObject1;
MyObject2.prototype.toString = function() { return "7654"; }

value = new MyObject2 + 1 /* 4568 */
value = new MyObject2 - 1 /* 4566 */

 



Method valueOf() wins again! So JavaScript looks for valueOf along complete prototype chain, and if not found it repeats search by chain for toString definition.


Now one thing that surprises me much. I know that Date has both valueOf and toString defined. First returns time in milliseconds before/after Jan 1, 1970, second return string representation of Date in UTC (Universal Time Coordinates). We just checked that JavaScript favors valueOf for addition/substraction. It evens make a hard work traversing prototypes chain to find valueOf. So how you explain this:

var YEAR_IN_MILLIS = 31536000000;
var value = new Date - 31536000000 /* 1121352801519 */
/* try new Date(1121352801519) and you get Jul 14, 2005 */
var value = new Date + 31536000000 /* "Fri Jul 14 17:55:40 UTC+0300 200631536000000" */

 


Where is any logic? You may check how Function object behaves in regard to addition / substraction operators. It is quite predictable.


Magic Array arithmetic.


I skipped Array type in sections above for reason. Firstly, I've included it along the Function object. But later I understand that it's behavior should be considered separately.


Now we have big baggage of JavaScript arithmetic knowledge. We know that JavaScript always favor string concatenation when one of operands in addition is string. If JavaScript has no way but performs numeric arithmetic it favors valueOf function to get numeric value (let us keep strange Date object aside). If found, it uses valueOf. If not it tries to parse stringified object presentation.



Array type has identical valueOf() and toString() implementation, that simply returns string of elements concatenated via comma (","). Hence empty Array will produce empty string, single element Array will produce stringified form of sole value, any more elements yield string with commas. Now you must understand the following yourself:




[] * 1 === "" * 1 === 0 * 1 === 0; // (sorry forget to mention that empty string is zero)
* 1 === "5" * 1 === 5;
* 1 === "5,6" * 1 is NaN;


[] + 256 === "" + 256 === "256" // notice quotes
+ 256 === "5" + 256 === "5256"
+ 256 === "5,6" + 256 === "5,6256"// this comma is not numbers separator :wink:
[] - 1 === "" - 1 === 0 - 1 === -1;
- 1 === "5" - 1 === 4;
- 1 === "5,6" - 1 is NaN;

 


Lessons learned


String is very tricky type in regard to arithmetic operations. If one operand in addition is string, JavaScript uses string concatenation. For all other cases JavaScript tries to parse number from string. Not explicitly mentioned, but 99 / "3" === 33. Division by empty string or "0" yields Infinity.
For Object, Date, Array, Function JavaScript and custom types uses implicit conversion to number using pair of valueOf / toString methods. Normally, valueOf wins regardless of prototype chain depth. But there is an exception for Date object (Could someone point me to the line in ECMA specification???). The way Array.prototype.valueOf method is implemented may cause unpredictable result in arithmetic operations. If you want to become a personal enemy for your co-workers, then use this feature intensively. Same is true for custom objects arithmetic that depends on "smart" valueOf().

    1. If no suitable conversion to number available result will be always NaN. The only safe way to check NaN
value is via calling isNaN(arg) built-in function.

    1. There is very handy method to convert string with numeric presentation to number, just multiply it by one.

    2. There is very handy method to get milliseconds from Date object, just multiply Date by one.


Till next time when I will consider logical operators. I'm sure, now you guess that they are applicable not only to Boolean values...

1 Comment