**Introduction**

JavaScript is a dynamic weakly-typed language so it’s possible to have expressions like this:

var foo = "string" + 22 * 3 - 4;This post explains how JavaScript evaluates such complex ‘mix-n-matches’ and at the end of this, you should know why

fooisNaN.First, a screenshot showing more funny behaviour:

A brief Maths Refresher

AssociativityThe result of the mathematical operation is always same regardless of the ‘

consumption’order of the operandsduring the operation. Associativity deals with the operators and is important in resolving situations that involve an operand between two operators. In the examples below, there is always a number between the two mathematical operators. Associativity rules remove the ambiguity that might arise in these situations.Addition and multiplication are associative operations.

(1 + 2) + 3 = 1 + (2 + 3); (1 * 2) * 3 = 1 * (2 * 3);

Side Note:Mathematical operations on floating point values (IEEE 794) suffer from rounding errors and can give funny results.

Non-associativityOrder matters, opposite of associativity. Operations could be left-associative or right-associative.

5 - 3 - 2 = (5 - 3) - 2; //left associativity var a = b = 7; // a = (b = 7); //right associativity

CommutativityThe result of the mathematical operation is always the same regardless of the position of the operands. Commutativity, as opposed to associativity, focuses more on the operands – if swapping the place of operands does not affect the result then it is commutative. As again, addition and multiplication are commutative (and associative as well) while division and subtraction are not.

1 + 2 = 2 + 1; //commutative 3 * 5 = 5 * 3; //commutative 1 - 2 != 2 - 1; //not commutative

Mathematics and Programming: The Interesting DivideOperators can be overloaded in Mathematics and programming and in both cases the input values (i.e. operands) determine the right operation. For example, the multiplication symbol

Xcan either signify pure arithmetic multiplication if both values are numbers or a vector cross-product if both inputs are vectors or even scalar vector multiplication. Similarly in programming, the+operator is usually overloaded to mean both addition and string concatenation, depending on context and usage.Overloading has constraints; for example, the expression 1 + “boy” is invalid (and quite absurd) in the mathematics realm; operands have to be members of well-defined sets in other to get meaningful results.

Operators in strongly-typed programming languages, like their Mathematical counterparts, only allow operations on compatible types. Programmers have to explicitly coerce types to expected values if they want to mix and mash.

Weakly-typed languages offer no such restrictions, rather they attempt to automatically deduce the programmer’s intent and coerce values based on some heuristics. As expected, surprises occur when the language’s interpretation differs from the programmer’s intentions.

For example, consider the expression 1 + “2” in a weakly-typed programming language, this is ambiguous since there are two possible interpretations based on the operand types (int, string) and (int int).

- User intends to concatenate two strings, result: “12”
- User intends to add two numbers, result: 3

The only way out of the conundrum is the use of operator precedence and associativity rules – these determine the result.

**How JavaScript adds numbers**

Steps in the addition algorithm

- Coerce operands to primitive values

The JavaScript primitives are *string, number, null, undefined *and** ***boolean (Symbol is coming soon in ES6)*. Any other value is an *object (e.g. **arrays, functions and objects)*. The coercion process for converting objects into primitive values is described thus:

* If a primitive value is returned when object.valueOf() is invoked, then return this value, otherwise continue

* If a primitive value is returned when object.toString() is invoked, then return this value, otherwise continue

* Throw a TypeError

**Note: **For date values, the order is to invoke toString before valueOf.

- If any operand value is a string, then do a string concatenation
- Otherwise, convert both operands to their numeric value and then add these values

**The case for the unary + operator**

The unary + operator is quite different – it forcibly casts its single operand to a number.

//Cast to number +"3"; //Convert to string "" + 3;The first case uses the unary operator which will convert the string to a number while the second case casts to a string by passing a string as one of the operands to the addition operator.

But what about the – operator?Subtraction is great because it is not overloaded to signify other operations; when used, the intent is always to subtract the RHS from the LHS. Thus, both operands are converted to numbers and then subtraction is carried out on the numeric values. And this is why you can use the –

operator to cast values too.Trying to subtract a string of characters from another string of characters is undefined and you’ll always get a NaN.

"3" - ""; ; 3 //Relying on implicit conversion in - operator

Examples

The table of coercionsFirst, a table showing the generated values from coercion operations. This makes it very easy to deduce the result of mix-n-mash expressions.

Primitive Value | String value | Numeric value |
---|---|---|

null | “null” | 0 |

undefined | “undefined” | NaN |

true | “true” | 1 |

false | “false” | 0 |

123 | “123” | 123 |

[] | “” | 0 |

{} | “[object Object]” | NaN |

**Examples – The fun starts**

Some examples, try to see if you can explain the results. Believe me, this is a fun fun ride. Enjoy!

1 + 2;

Output:3

Why?:Addition of two numbers'1' + 2;

Output:’12’

Why?: Addition of a number and a string – both operands are converted to strings and concatenated.2 - 1;

Output: 1

Why?: Subtraction of two numbers'2' - 1;

Output: 1

Why?: Subtraction of a number from a string – both operands are converted into numeric values2 - '1a';

Output: NaN

Why?: Subtraction of a string from a number – conversion of ‘1a’ into a number value gives NaN and any Maths op involving a NaN gives a NaN.2 + null;

Output: 2

Why?: Addition of a number and the null primitive, numeric value of null primitive is 0 (see table of coercions). 2 + 0 is 2.2 + undefined;

Output: NaN

Why?: Addition of a number and theundefinedprimitive – numeric value ofundefinedprimitive is NaN (see table of coercions) and operations involving a NaN give a NaN.2 + true;

Output: 3

Why?: Addition of a number and thetrueprimitive – numeric value oftrueprimitive is 1 (see table of coercions). 2 + 1 = 3.2 + false;

Output: 2

Why?: Addition of a number and thefalseprimitive – numeric value of thefalseprimitive is 0 (see table of coercions). 2 + 0 = 2.

Fun with objectsThe preceding part covered mostly primitives (with the exception of strings), now on to the big objects; pun intended.

First objects2 + {};

Output:2[object Object]

Why?:{}.toValue returns {} (which is not a primitive) so {}.toString() is invoked and this returns the string ‘[object Object]’. String concatenation occurs.{} + 2;

Output:2

Why?:This one is quite tricky I admit. JavaScript sees the {} as an empty execution block, so technically the above sample is equivalent to + 2 which is 2.var a = {}; a + 2;

Output:[object Object]2

Why?:The assignment removes the ambiguity – JavaScript knows for sure it is an object literal. The rules of conversion follow as earlier described.

Arrays next!2 + [];

Output:“2”

Why?:[].toValue returns the array (which is not a primitive) hence [].toString() is invoked and this returns the empty string. The operation is now 2 + “” and this results in string concatenation.[] + 2;

Output:“2”

Why?:Same as above

Associativity and Evaluation

JavaScript + operator is left-associative, this means operands are evaluated from left to right when they occur more than once in a series. Thus 1 + 2 + 3 in JavaScript (being left-associative) will be evaluated as (1 + 2) + 3 and so on. You can read more here.

Now to the samples again!

1 + 2 + "3";

Output:“33”

Why?:left-associativity ensures this is (1 + 2) + “3”, which goes to 3 + “3”, giving 331 + "2" + 3;

Output:“123”

Why?:This will be evaluated as (1 + “2”) + 3, and then “12” + 3"1" + 2 + 3;

Output:“Left as an exercise ;)”.

Why?:Share your answer in the comments.

ConclusionThis post was actually motivated by Gary Bernhardt’s very popular WAT talk, at this stage I hope you have gained the following:

- Ability to fearlessly refactor JavaScript code that is lacking parentheses or has no clear operator/operand ordering.
- A deeper understanding of how JavaScript evaluates expressions and operations on primitives and object types

Do let me know your thoughts in the comments!

left-associativity ensures this is (“1” + 2) + 3, so “12” + 3 gives 123.

LikeLike

Perfect! You got it!! :)

LikeLike

First, thanks for this great post, it made me clear about coercion, except {} and [] part, I couldn’t understood, please tell me more about .toValue and to.String, why object and array first try to evaluate .toValue and then later to .toString

LikeLike

btw. great examples, thanks!

LikeLike

Thanks for the feedback – glad that you liked it!

LikeLike

What is the .toValue property ?

Is it documented anywhere in ECMA script ?

LikeLike

Thanks Rachit

Yes, the GetValue method spec. You can read more here:

https://es5.github.io/#x8.7.1

LikeLike

Another amusing take on JavaScript’s oddities: https://www.destroyallsoftware.com/talks/wat

LikeLike

Thanks Dave! That talk actually motivated this post :)

LikeLike