Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Mysteries of type coercion

As this question appears fairly often on the forums, I figured I'd provide more info for those interested.

In JavaScript, you have two equality operators. One is popularly called the equality operator (==). The ECMAScript Language Specification officially calls it The Equals Operator.

This operator follows a certain algorithm when comparing values. The algorithm is called The Abstract Equality Comparison Algorithm and its explanation can be found here.

In short, it first checks if the values are of the same type. If they are, it returns true for Undefined and Null. For numbers it returns false if the numbers are different or if one of them is NaN, because according to the IEEE 754 specification NaN is not equal to itself. For strings, it checks if they're the same sequence of characters and returns true if they are, otherwise it returns false. For boolean values it returns true if they're both the same value. For objects, it returns true if they refer to the same object.

If the values are not of the same type, it may or may not do type coercion.

For null and undefined no actual coercion is done, the algorithm just returns true if one value is undefined and the other is null.

If one of the values is a number and the other is a string, the string gets converted to a number using the abstract operation ToNumber. Then they are compared as numbers.

ToNumber is also used on Boolean values. The result of the operation is 1 for true and +0 for false.

For objects, an abstract operation ToPrimitive is used and then the converted object is compared to the other value. ToPrimitive is a bit more complex, so I won't get into it.

Finally, for any other case, it returns false.

The other comparison operator, also known as the identity operator (===) is officially known as The Strict Equals Operator.

It follows a much simpler algorithm called The Strict Equality Comparison Algorithm.

If the types of the compared values are different, it returns false.

For Undefined it returns true.

For Null it returns true.

For numbers it returns false for NaN and if the numbers are different.

For strings, it returns true for the same sequence of characters.

For boolean values, it returns true if they're both true, or both false.

For objects, it returns true if both variables refer to the same object.

A common misconception is that the identity operator (===) is slower because it also checks for type as well as value. This is not true. In fact, the equality operator (==) is the slower one as it enables type coercion and has to do more things.

They both check for the type of the value, it's just that the equality operator takes additional steps to compare them.

While all of this might seem a bit complex, as you learn more and more JavaScript (and programming in general) you'll find that this algorithm is fairly simple. Read through it a few times, maybe draw a diagram to help you understand how it works. Knowing such things is worth it in the long run.

Finally, regardless of what some books (cough JavaScript: The Good Parts cough) might say, it's perfectly fine for you to use the equality (==) operator, as long as you know how it works and when it's appropriate.

2 Answers

Dave McFarland
STAFF
Dave McFarland
Treehouse Teacher

Excellent description Dino Paškvan !

For those visually inclined here's code for some of the common examples you might run into:

undefined == null // true
undefined === null // false
1 == '1' // true
1 === '1' // false
1 == true // true
1 === true // false
0 == false // true
0 === false // false

For anyone interested in how the abstract ToPrimitive operation works (in the context of equality comparison), I'm providing this example:

// Three constructors that do the same thing
function WithToString(num, str) {
  this.num = num;
  this.str = str;
}

function WithValueOf(num, str) {
  this.num = num;
  this.str = str;
}

function WithBoth(num, str) {
  this.num = num;
  this.str = str;
}

// Different overrides for toString and valueOf
WithToString.prototype.toString = function () {
  return this.str;
}

WithValueOf.prototype.valueOf = function () {
  return this.num;
}

WithBoth.prototype.toString = function () {
  return this.str;
}

WithBoth.prototype.valueOf = function () {
  return this.num;
}

// Instance three new objects from the constructors above, using the same parameters
var withToString = new WithToString(5, "test");
var withValueOf = new WithValueOf(5, "test");
var withBoth = new WithBoth(5, "test");

console.log(withToString); // { num: 5, str: 'test' }
console.log(withToString.toString()); // test
console.log(withToString.valueOf()); // { num: 5, str: 'test' }
console.log(withValueOf); // { num: 5, str: 'test' }
console.log(withValueOf.toString()); // [object Object]
console.log(withValueOf.valueOf()); // 5
console.log(withBoth); // { num: 5, str: 'test' }
console.log(withBoth.toString()); // test
console.log(withBoth.valueOf()); // 5

withToString == "test"; // true
withToString == 5; // false
withValueOf == "test"; // false
withValueOf == 5; // true
withBoth == "test"; // false
withBoth == 5; // true
withBoth == "5"; // true

I've written three constructors that do the same thing, but I've overridden the default Object.prototype.toString and Object.prototype.valueOf methods in different ways. The names of the constructors and instanced objects describe which of those two methods I've overridden.

As you can see from the output of the console.log() statements, the overridden methods now output object properties.

When comparing the equality of these objects with strings and numbers, coercion happens through these methods. The object will always try to be coerced into a number through valueOf, if that's not possible, it gets coerced through toString.

It's interesting to note that the withBoth object is not equal to the string "test" even though it has an overridden method as the coercion prefers valueOf. What is more interesting is that it is equal to the string "5". This is because that string gets coerced into a number, so they appear equal.

Finally, if you were to compare an object whose valueOf returned 1 to a boolean true, that comparison would be true because, as mentioned above, the boolean value gets coerced to 1.