The interviewer, with a calm smile, has just typed a single line of code into the shared editor:
console.log([] == ![]);
You’re asked to explain the above.
Now, if you’re anything like me, you see this as a beautiful puzzle waiting to be solved. You enjoy the system of step-by-step decoding.
JavaScript’s == is notorious for confusing developers. But once you understand how coercion works and follow a consistent set of steps, you can crack any == comparison like a pro. You’ll want more!
I’m going to give you everything you’ll ever need.
Before we dive into it, I have some JS Essentials for you. You won’t be able to effectively apply the rules before you understand these.
1. Truthy vs Falsy Values
There are only 7 falsy values in JavaScript:
false, 0, -0, '', null, undefined, NaN
Everything else is truthy, including:
-
[](empty array) -
{}(empty object) -
function() {}(functions) -
'0'(non-empty string) Infinity
So, if ([]) console.log('yes') will log 'yes'.
2. ToPrimitive: How Objects Become Primitives
This is the secret handshake. When JavaScript compares an object to a primitive using ==, it must first convert the object into a primitive. This is done via the internal ToPrimitive operation. ( Unless overwritten by an internal method. See the final example.)
It tries in order:
valueOf()- If that returns an object, then
toString()
This is why arrays often behave weirdly:
💡 Examples:
[] == ''
// [].toString() -> '' → '' == '' → true
[1] == '1'
// [1].toString() -> '1' → '1' == '1' → true
({} == '[object Object]')
// → ToPrimitive({}) → '[object Object]'
// '[object Object]' == '[object Object]' → true
+[] // 0 (unary + calls ToNumber, [] becomes '')
+{} // NaN ({} becomes '[object Object]' → NaN)
3. Unary Plus (+) Prefers Numbers
When you do +value, JavaScript will convert to a number. This includes objects:
+'' // 0
+true // 1
+[] // 0
+{} // NaN
+null // 0
+undefined // NaN
Did you get it?
+[]
// ToPrimitive([]) → ‘’
// ToNumber(‘’) → 0
+{}
// ToPrimitive({}) → ‘[object Object]’
// ToNumber(‘[object Object]’) → NaN
Quick Challenge
What’s the result of [''] == ''?
Think it through step-by-step using the ToPrimitive rule. Don’t guess. The goal of this guide is to replace memorization with a system.
🧠 The Sauce: Solve Any == Step-by-Step
Let’s walk through a foolproof method to evaluate any weird-looking coercion expression like [] == ![] or null == undefined.
1. If the types are the same, compare normally (like ===).
No coercion needed.
2. If one is null and the other is undefined, it’s true.
This is a special, isolated rule. They are only equal to each other.
null == undefined // true
null == 0 // false
undefined == '' // false
3. If one is a Number and the other a String, convert the String to a Number.
100 == "100" // "100" becomes 100. -> true
"" == 0 // "" becomes 0. -> true
" 42" == 42 // " 42" becomes 42. -> true
4. If one is a Boolean, convert it to a Number and restart the comparison.
This is a key source of confusion. true becomes 1, false becomes 0. The comparison then starts over from Step 1 with the new value.
true == 1 // true becomes 1. Expression is now 1 == 1. -> true
false == 0 // false becomes 0. Expression is now 0 == 0. -> true
// A multi-step example:
true == "1"
// First, true becomes 1.
// The expression is now 1 == "1".
// Now Rule #3 applies: "1" becomes 1.
// Finally, 1 == 1. -> true
5. If one is an Object and the other a Primitive, convert the Object to a Primitive.
This uses the ToPrimitive operation we learned earlier. The comparison then restarts.
// Example 1: Array and String
[] == ''
// The array [] is an Object.
// ToPrimitive([]) results in an empty string "".
// The expression becomes "" == "". -> true (via Rule #1)
// Example 2: Object and String
{} == '[object Object]'
// The object {} is an Object.
// ToPrimitive({}) results in the string '[object Object]'.
// The expression becomes '[object Object]' == '[object Object]'. -> true (via Rule #1)
Let’s try a few:
1.[] == ''
- Array gets toString() → ''
- '' == '' → true
2.[] == 0
-
[] → ''(ToPrimitive) - '' → 0 (string to number)
- 0 == 0 → true
3.[1] == true
- [1] → '1' → '1' == true
- true → 1, '1' → 1
- 1 == 1 →
true
4.{} == '[object Object]'
- {} → '[object Object]'
- '[object Object]' == '[object Object]' → true
[] == ![]
With our new rules, this is child’s play.
- Right side first: ![] is false. (An empty array is a “truthy” object).
- Our problem:
[] == false. - Rule #4 (Boolean Rule): The boolean false becomes the number 0.
- Our problem now:
[] == 0. - Rule #5 (Object Rule): The object [] tries to become a primitive. It falls back to toString(), becoming an empty string “”.
- Our problem now:
"" == 0. - Rule #3 : The string “” becomes the number 0.
- Final state:
0 == 0. - Answer:
true.
No magic. No memorization. It’s simple and predictable. (derogatory)
👹 The Final Boss: The Diabolical Interview Question
You’re acing it. The interviewer is impressed. Now they bring out the final test. They show you this:
const myObject = {
i: 10,
valueOf: function() {
return this.i++;
}
};
// They ask for the result of these three lines, in order:
console.log(myObject == 10); // ?
console.log(myObject == 11); // ?
console.log(myObject == 12); // ?
A normal developer panics. They see an object that seems to magically equal three different numbers. It looks like an impossible paradox.
You smile.
You see the valueOf method and you remember the full secret behind Rule #5 (The Object Rule): The ToPrimitive algorithm looks for valueOf() before it ever tries toString(). This object has hijacked the coercion process, and you know exactly how to handle it.
Let’s break it down, line by line.
- First Comparison: myObject == 10
- Start: myObject == 10 (Object vs. Number).
- Apply Rule #5: Convert myObject to a primitive. The engine finds the custom valueOf() method and calls it.
- Execution: myObject.valueOf() runs. It returns the current value of i, which is 10. Then, as a side effect, it increments i to 11.
- New State: The expression becomes 10 == 10.
- Apply Rule #1: The types are the same.
- Result: true.
I’ll leave the other two for you.
Go ace that interview ✨
Top comments (0)