Introduction: The Problem That Blew Up Stack Overflow
\n\nImagine this: you're in an interview, and the interviewer asks with a sly smile: "Can the expression a == 1 && a == 2 && a == 3 ever return true?". Your first reaction — "Of course not! One variable cannot simultaneously equal three different numbers."
However, if you know JavaScript a little deeper, you know: in this language, the most unexpected things are possible. This question gathered thousands of views on Stack Overflow and became a classic for testing understanding of type coercion, getters, and object wrappers in JS.
\n\nIn this article, we will not only prove that this is possible but also break down 5 different ways to make (a==1 && a==2 && a==3) return true. You will learn how loose comparison (==) works, how it differs from strict comparison (===), and how you can control it.
This article will be useful both for beginners who want to understand JavaScript more deeply, and for experienced developers preparing for an interview. Let's go!
\n\nMethod #1: The Magic of valueOf (Most Popular)
\n\nThe most elegant and frequently cited way is to use a custom object with an overridden valueOf method. In JavaScript, when you compare an object with a primitive using ==, the engine automatically calls valueOf() (or toString()) to convert the object to a primitive.
We will create a counter object that increments its value by 1 each time valueOf is called.
const a = {\n value: 1,\n valueOf() {\n return this.value++;\n }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true\n\nHow it works:
\n- \n
- On the first comparison
a == 1,valueOf()is called, returns 1, andvalueincrements to 2. \n - On the second comparison
a == 2,valueOf()is called again, returns 2,valuebecomes 3. \n - The third comparison
a == 3completes the chain successfully. \n
This is a perfect example of type coercion in action. It is this mechanism that makes JavaScript both a flexible and a dangerous language.
\n\nMethod #2: toString — The Twin of valueOf
\n\nIf you override toString instead of valueOf, the result will be the same. JavaScript can use either of these methods for object-to-primitive conversion during loose comparison.
const a = {\n value: 1,\n toString() {\n return this.value++;\n }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true\n\nThe difference is subtle: valueOf has priority for numeric comparisons, while toString has priority for string comparisons. But in our case, both work perfectly.
Method #3: Getters in the Global Scope
\n\nMany people don't even realize that global variables in JavaScript are actually properties of the global object (window in browsers, global in Node.js). And properties can be defined using getters.
We can define a getter for the variable a directly on the global object:
let value = 1;\nObject.defineProperty(window, 'a', {\n get() {\n return value++;\n }\n});\n\nconsole.log(a == 1 && a == 2 && a == 3); // true\n\nThis method is particularly interesting because a behaves like a regular variable, although it is actually a property with a getter. Every time you access a, the getter returns a new value.
Note: This code works in the browser environment. In Node.js, you would use global instead of window.