Question
How is it possible for the following JavaScript expression to evaluate to true?
(a == 1 && a == 2 && a == 3)
This is sometimes asked as an interview question. Although code like this is not something we would normally write in real projects, it raises an important question about how JavaScript evaluates ==, type coercion, and object-to-primitive conversion.
Short Answer
By the end of this page, you will understand why a == 1 && a == 2 && a == 3 can evaluate to true in JavaScript, how loose equality (==) performs type coercion, and how objects can control their primitive value using methods such as valueOf, toString, or Symbol.toPrimitive. You will also learn why this is mostly a language quirk rather than a pattern to use in production code.
Concept
JavaScript has two important ideas behind this puzzle:
- Loose equality (
==) performs type coercion - Objects can define how they convert to primitive values
When JavaScript compares an object to a number using ==, it tries to convert the object into a primitive value first. That primitive might come from:
Symbol.toPrimitivevalueOf()toString()
If that conversion returns a different value each time, then the same variable can appear equal to different numbers in separate comparisons.
For example, if a is an object whose primitive value changes each time it is read, then these can all be true in sequence:
a == 1a == 2a == 3
Because && evaluates left to right, JavaScript checks each comparison one after another.
This matters because it shows that JavaScript values are not always simple primitives like numbers or strings. Objects can participate in comparisons in surprising ways. Understanding this helps you avoid bugs and write safer code, especially when using ==.
In real programming, the lesson is not "how do I trick JavaScript?" but rather:
Mental Model
Think of a as a person being asked the same question three times:
- First time: "What number are you?" → answers
1 - Second time: "What number are you?" → answers
2 - Third time: "What number are you?" → answers
3
If a is just a normal number, it cannot do that.
But if a is an object with custom conversion logic, it can give a different answer each time JavaScript asks for its primitive value.
So the key idea is:
- a primitive value is fixed
- an object can decide how to behave when converted
Syntax and Examples
The most common way to make this happen is to use an object with a custom valueOf() method.
let count = 0;
const a = {
valueOf() {
count += 1;
return count;
}
};
console.log(a == 1 && a == 2 && a == 3); // true
Why this works
Each time a is compared with a number using ==, JavaScript asks the object for a primitive value.
The valueOf() method runs each time:
- first call returns
1 - second call returns
2 - third call returns
3
So the full expression becomes effectively:
true && true && true
Using Symbol.toPrimitive
Step by Step Execution
Consider this code:
let count = 0;
const a = {
valueOf() {
count += 1;
return count;
}
};
const result = a == 1 && a == 2 && a == 3;
console.log(result);
Step-by-step trace
countstarts as0.- JavaScript evaluates
a == 1. ais an object, so JavaScript tries to convert it to a primitive.valueOf()runs:countbecomes1- returns
1
- Comparison becomes
1 == 1, which istrue. - Because the left side of
&&istrue, JavaScript continues.
Real World Use Cases
You should almost never use this exact pattern in production, but the underlying concepts are real and useful.
1. Custom object conversion
Libraries sometimes define how objects behave when converted to strings or numbers.
Examples:
- date-like objects converting to timestamps
- money objects converting to numeric values
- logging helpers converting to readable strings
2. Implicit coercion bugs
A developer may accidentally compare an object with a primitive using == and get surprising results.
Understanding this puzzle helps you recognize those bugs.
3. Framework and library internals
Some libraries implement custom serialization or primitive conversion for convenience. That does not usually mean changing values on each access, but it uses the same mechanism.
4. Debugging strange comparisons
If a comparison behaves unexpectedly, checking valueOf, toString, or Symbol.toPrimitive can reveal the cause.
5. Interview and language knowledge checks
This example is often used to test whether someone understands:
- loose equality
- coercion rules
- object conversion
- short-circuit evaluation
Real Codebase Usage
In real codebases, developers usually avoid clever coercion tricks and instead aim for predictable behavior.
Common patterns
Prefer strict equality
if (status === 200) {
// predictable comparison
}
This avoids hidden conversions.
Use explicit conversion
const input = "42";
const value = Number(input);
if (value === 42) {
console.log("Matched");
}
Guard against unexpected object values
function isValidAge(age) {
if (typeof age !== "number") {
return false;
}
return age >= 0;
}
Use custom conversion carefully in domain objects
class Money {
() {
. = amount;
}
() {
.;
}
}
price = ();
.(price > );
Common Mistakes
1. Assuming == and === behave the same
Broken expectation:
const a = {
valueOf() {
return 1;
}
};
console.log(a == 1); // true
console.log(a === 1); // false
Avoid this by using === unless you intentionally want coercion.
2. Forgetting that objects can be converted
Beginners often think objects can never equal numbers.
With ==, JavaScript may convert the object first.
const a = {
valueOf() {
return 5;
}
};
console.log(a == 5); // true
3. Writing confusing clever code
Even if something works, it may be hard to read:
Comparisons
| Concept | Behavior | Coercion? | Can make the puzzle work? |
|---|---|---|---|
== | Loose equality | Yes | Yes |
=== | Strict equality | No | No |
valueOf() | Object returns primitive value | Yes, when needed | Yes |
toString() | Object returns string form | Yes, when used for conversion | Sometimes |
Symbol.toPrimitive | Explicit primitive conversion hook | Yes, highest priority |
Cheat Sheet
Key idea
a == 1 && a == 2 && a == 3 can be true if a is an object that returns a different primitive value each time it is converted.
Basic pattern
let count = 0;
const a = {
valueOf() {
return ++count;
}
};
console.log(a == 1 && a == 2 && a == 3); // true
Conversion hooks
JavaScript may use these when converting an object to a primitive:
Symbol.toPrimitivevalueOf()toString()
Equality rules
==allows coercion===does not allow coercion- objects compared with primitives may be converted first
&& rule
FAQ
Can a == 1 && a == 2 && a == 3 really be true in JavaScript?
Yes. If a is an object with custom primitive conversion logic that returns a different value each time, the expression can evaluate to true.
Does this work with === instead of ==?
No. Strict equality does not coerce an object into a primitive for equality comparison in this way.
Which methods control object-to-primitive conversion?
The main ones are Symbol.toPrimitive, valueOf(), and toString().
Why does JavaScript allow this?
JavaScript supports implicit type conversion for flexibility and backward compatibility. This can sometimes produce surprising results.
Should I ever use this in production code?
Usually no. It is mainly useful for understanding coercion rules and for interview discussion.
Is valueOf() always used first?
Not always. If Symbol.toPrimitive is defined, it generally has higher priority.
Why does && matter here?
Because each comparison is evaluated separately from left to right. That gives the object a chance to return a new value each time.
Mini Project
Description
Build a small JavaScript demo that shows how object-to-primitive conversion affects loose equality. The project helps you see exactly when conversion methods are called and why repeated comparisons can produce surprising results.
Goal
Create an object whose primitive value changes on each comparison, then compare it with numbers and log the results.
Requirements
- Create an object with custom primitive conversion behavior.
- Make the object return a different number each time it is compared.
- Evaluate
a == 1 && a == 2 && a == 3and print the result. - Log each conversion so you can see when it happens.
- Add one strict equality check to show the difference from loose equality.
Keep learning
Related questions
Adding Table Rows with jQuery: append(), Limits, and Best Practices
Learn how to add table rows in jQuery using append(), what elements are allowed in tables, and safer ways to build rows dynamically.
Bower vs npm: What’s the Difference in JavaScript Package Management?
Learn the plain difference between Bower and npm, how each manages packages, and why npm replaced Bower in most JavaScript projects.
Check If an Element Exists in jQuery
Learn how to check whether an element exists in jQuery using .length, truthy pitfalls, and practical patterns for real projects.