ChatGPT解决这个技术问题 Extra ChatGPT

Can (a== 1 && a ==2 && a==3) ever evaluate to true?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.

Moderator note: Please resist the urge to edit the code or remove this notice. The pattern of whitespace may be part of the question and therefore should not be tampered with unnecessarily. If you are in the "whitespace is insignificant" camp, you should be able to accept the code as is.

Is it ever possible that (a== 1 && a ==2 && a==3) could evaluate to true in JavaScript?

This is an interview question asked by a major tech company. It happened two weeks back, but I'm still trying to find the answer. I know we never write such code in our day-to-day job, but I'm curious.

Comments are not for extended discussion; this conversation has been moved to chat.
To the people that apparently voted to cloae this as too broad: is that a dig at Javascript, saying that there are too many valid answers?
Some people sit around philosophizing about what's possible. Others focus their efforts on whether or not they are building viable, business correct products for their clients. IMO, this question has no practical utility beyond the fact that you should never ask these kinds of questions in an interview or write this kind of code. That's why it should be closed. I mean really, does the business realize they paid somebody real money to sit around and talk about this stuff?
After reading the answers, the morals of the story are: don't use == when you mean ===, have a coding standard that bans non-ASCII variable names, and have a linting process which enforces the previous two morals.
Moderator note: Stack Overflow has had a history of people chiming in with answers in different languages to the one in question. These are attempts to answer the question because they are solutions to the general problem, albeit in a different language. Please refrain from flagging them as "not an answer". Having said that, please also refrain from posting more answers in different languages - there is a reason this question is specific to JavaScript, as pointed out by comments under some of these other answers, and there is a reason we like our language-specific questions to remain so.

u
user4020527

If you take advantage of how == works, you could simply create an object with a custom toString (or valueOf) function that changes what it returns each time it is used such that it satisfies all three conditions.

const a = { i: 1, toString: function () { return a.i++; } } if(a == 1 && a == 2 && a == 3) { console.log('Hello World!'); }

The reason this works is due to the use of the loose equality operator. When using loose equality, if one of the operands is of a different type than the other, the engine will attempt to convert one to the other. In the case of an object on the left and a number on the right, it will attempt to convert the object to a number by first calling valueOf if it is callable, and failing that, it will call toString. I used toString in this case simply because it's what came to mind, valueOf would make more sense. If I instead returned a string from toString, the engine would have then attempted to convert the string to a number giving us the same end result, though with a slightly longer path.


Could you achieve this by altering the implied valueOf() operation?
Yes, valueOf works in place of toString for the same reason
Comments are not for extended discussion; this conversation has been moved to chat.
According to this a number conversion will be tried first so valueOf is slightly better.
@Pureferret the left-hand side of the equality comparison is an object, not a number. That that object has a number property on i doesn't bother the engine. ;)
J
Jeff

I couldn't resist - the other answers are undoubtedly true, but you really can't walk past the following code:

var aᅠ = 1; var a = 2; var ᅠa = 3; if(aᅠ==1 && a== 2 &&ᅠa==3) { console.log("Why hello there!") }

Note the weird spacing in the if statement (that I copied from your question). It is the half-width Hangul (that's Korean for those not familiar) which is an Unicode space character that is not interpreted by ECMA script as a space character - this means that it is a valid character for an identifier. Therefore there are three completely different variables, one with the Hangul after the a, one with it before and the last one with just a. Replacing the space with _ for readability, the same code would look like this:

var a_ = 1; var a = 2; var _a = 3; if(a_==1 && a== 2 &&_a==3) { console.log("Why hello there!") }

Check out the validation on Mathias' variable name validator. If that weird spacing was actually included in their question, I feel sure that it's a hint for this kind of answer.

Don't do this. Seriously.

Edit: It has come to my attention that (although not allowed to start a variable) the Zero-width joiner and Zero-width non-joiner characters are also permitted in variable names - see Obfuscating JavaScript with zero-width characters - pros and cons?.

This would look like the following:

var a= 1; var a‍= 2; //one zero-width character var a‍‍= 3; //two zero-width characters (or you can use the other one) if(a==1&&a‍==2&&a‍‍==3) { console.log("Why hello there!") }


Judging by the odd spacing in the original question, I think this is EXACTLY the answer the interview question was looking for - exploiting non-space characters that look like spaces. Good spot!
@Baracus It was RonJohn who noticed the weird spacing in his comment on Kevin's answer which reminded me of this (awful) technique, so I can't take credit for spotting it. I was kinda surprised noone had already answered with this though, as it went around my work a few years ago because of a blog post somewhere - I kinda assumed it was pretty common knowledge by now.
Of course, this is banned as a standard loophole, which also applies to interviews. [citation needed]
Considering the original spacing, it might be even worse, i.e. a variable var ᅠ2 = 3 has been used; so there are the three variables aᅠᅠ= 1, ᅠ2 = 3, a = 3 (a␣ = 1, ␣2 = 3, a = 3, so that (a␣==1 && a==␣2 && a==3))…
@AL-zami there is an extra character in two of the variables, which shows on your screen as a space, but is interpreted as part of the identifier, meaning there are three separate variables - a, a and a - the extra character is the Hangul half-width space.
P
Pac0

IT IS POSSIBLE!

var i = 0; with({ get a() { return ++i; } }) { if (a == 1 && a == 2 && a == 3) console.log("wohoo"); }

This uses a getter inside of a with statement to let a evaluate to three different values.

... this still does not mean this should be used in real code...

Even worse, this trick will also work with the use of ===.

var i = 0; with({ get a() { return ++i; } }) { if (a !== a) console.log("yep, this is printed."); }


Yes I was trying the same thing :) So the correct answer in the interview would be, "It cannot happen in my code because I never use with."
@Pointy - And, I program in strict mode where with is not allowed.
@Pointy in the accepted answer they do something similar without the with so it can happen
@jorrit no one would use ==. And === prevents the accepted answer
@JonasW. A lot of people still use == but I haven't seen with since ... well actually never outside of JS documentation where it says "please don't use that". Anyway, a nice solution.
4
4 revs

Example without getters or valueOf:

a = [1,2,3]; a.join = a.shift; console.log(a == 1 && a == 2 && a == 3);

This works because == invokes toString which calls .join for Arrays.

Another solution, using Symbol.toPrimitive which is an ES6 equivalent of toString/valueOf:

let i = 0; let a = { [Symbol.toPrimitive]: () => ++i }; console.log(a == 1 && a == 2 && a == 3);


without valueOf, well... its more indirect but basically the same thing.
I really like this solution because you don't override anything but the objects own join function, and it's just a very clean and easy to read hack that makes the logic evaluate to true.
Honestly I think this is the best answer. It involves nothing out of the ordinary, just setting a few values. Very easy to understand even with basic JS knowledge. Well done.
This makes so much sense it almost feels useful.
I knew most answers would be about abusing toString or valueOf but this one caught me completely out of guard. Very clever and I didn't know it did call .joininternally, but it makes total sense.
P
Peter Mortensen

If it is asked if it is possible (not MUST), it can ask "a" to return a random number. It would be true if it generates 1, 2, and 3 sequentially.

with({ get a() { return Math.floor(Math.random()*4); } }){ for(var i=0;i<1000;i++){ if (a == 1 && a == 2 && a == 3){ console.log("after " + (i+1) + " trials, it becomes true finally!!!"); break; } } }


I would deliberately give this answer even if I knew the other solutions, because it answers the question but is obviously not what they were after. Play stupid games, win stupid prizes.
But what if it takes more than 1000 trials?
@Piyin If it takes more than 1000 trials you win a prize!
I like this answer because taking it to the extreme suggests that this is possible in any language if the cpu's registers/cache get hit with enough cosmic rays while the program is running, or if one deliberately performs a power glitch such that the failure branch of the if conditional doesn't actually jump.
Lowest: 1, Highest: 412.
K
Kos

When you can't do anything without regular expressions:

var a = { r: /\d/g, valueOf: function(){ return this.r.exec(123)[0] } } if (a == 1 && a == 2 && a == 3) { console.log("!") }

It works because of custom valueOf method that is called when Object compared with primitive (such as Number). Main trick is that a.valueOf returns new value every time because it's calling exec on regular expression with g flag, which causing updating lastIndex of that regular expression every time match is found. So first time this.r.lastIndex == 0, it matches 1 and updates lastIndex: this.r.lastIndex == 1, so next time regex will match 2 and so on.


@Abdillah a regex object will remember the last index it matches, call exec again will begin searching from that index. MDN is not very clear.
I see, so the this.r regex object remember the state / index. Thanks!
I would recommend to pass a string to exec though, not an integer to be stringified.
use regex and now you have two problems
m
mehulmpt

This is possible in case of variable a being accessed by, say 2 web workers through a SharedArrayBuffer as well as some main script. The possibility is low, but it is possible that when the code is compiled to machine code, the web workers update the variable a just in time so the conditions a==1, a==2 and a==3 are satisfied.

This can be an example of race condition in multi-threaded environment provided by web workers and SharedArrayBuffer in JavaScript.

Here is the basic implementation of above:

main.js

// Main Thread

const worker = new Worker('worker.js')
const modifiers = [new Worker('modifier.js'), new Worker('modifier.js')] // Let's use 2 workers
const sab = new SharedArrayBuffer(1)

modifiers.forEach(m => m.postMessage(sab))
worker.postMessage(sab)

worker.js

let array

Object.defineProperty(self, 'a', {
  get() {
    return array[0]
  }
});

addEventListener('message', ({data}) => {
    array = new Uint8Array(data)
    let count = 0
    do {
        var res = a == 1 && a == 2 && a == 3
        ++count
    } while(res == false) // just for clarity. !res is fine
    console.log(`It happened after ${count} iterations`)
    console.log('You should\'ve never seen this')
})

modifier.js

addEventListener('message' , ({data}) => {
    setInterval( () => {
        new Uint8Array(data)[0] = Math.floor(Math.random()*3) + 1
    })
})

On my MacBook Air, it happens after around 10 billion iterations on the first attempt:

https://i.stack.imgur.com/wjNp9.png

Second attempt:

https://i.stack.imgur.com/wFrAt.png

As I said, the chances will be low, but given enough time, it'll hit the condition.

Tip: If it takes too long on your system. Try only a == 1 && a == 2 and change Math.random()*3 to Math.random()*2. Adding more and more to list drops the chance of hitting.


Honestly, this is the best answer. All the other answer require a deliberate attempt to do something deeply unintuitive. This answer actually reflects something that might happen in the real world - a race condition.
Not only that - I've actually seen this happen in the real world. Not with the exact condition in the question, but certainly with checking (a==1) at the start of a function and (a==2) later in the function, and having code hit both conditions. FYI, the first time I saw this happening was in a car engine controller, and we put coding standards in place. The second time was in a chaff and flare dispenser system for military aircraft, and on my very first day at the company I found this and fixed it, while the rest of the team were still discussing the problem. (Kudos level: high! :)
So, you've worked on "car engine controllers" and "chaff and flare dispenser systems" which are programmed in javascript with web workers? I don't think I'll be going outside again.
@psaxton :) Of course not - but we have multi-threaded software with shared data. This is an anti-pattern for all multi-threaded software, not specific to Javascript or to web workers. It doesn't matter whether you're programming in assembly language, Brainf*ck, Visual BASIC, C or Javascript - if you do this with shared data in a multi-threaded application, it will always fail.
I think this is now an elaborate wrapper around @jontro's answer.
j
jontro

It can be accomplished using the following in the global scope. For nodejs use global instead of window in the code below.

var val = 0; Object.defineProperty(window, 'a', { get: function() { return ++val; } }); if (a == 1 && a == 2 && a == 3) { console.log('yay'); }

This answer abuses the implicit variables provided by the global scope in the execution context by defining a getter to retrieve the variable.


This assumes a is a property of this which it does not appear to be. If a was a local variable (which it looks like), then this would not work.
@jfriend00 you mean if you placed var a; somewhere?
Yeah. Referencing a == 1 implies than a is a variable somewhere, not a property of this. While there is an oddball place like globals where both could be true, generally, declaring a variable with var a or let a means there's no this that lets you access a as a property like you're code assumes. So, your code is apparently assuming some weird global variable thing. For example, your code does not work in node.js and not in strict mode inside a function. You should specify the exact circumstances where it works and probably explain why it works. Otherwise, it's misleading.
@jfriend00 well sure. Not sure that it would add much more value in combination with the other already answers. Will update the answer
The question was, could this "ever" be true. And the answer is yes, and this is one of the scenarios where it might be true: a is not a local variable and is defined on the global scope with an incrementing getter.
C
Cerbrus

This is also possible using a series of self-overwriting getters:

(This is similar to jontro's solution, but doesn't require a counter variable.)

(() => { "use strict"; Object.defineProperty(this, "a", { "get": () => { Object.defineProperty(this, "a", { "get": () => { Object.defineProperty(this, "a", { "get": () => { return 3; } }); return 2; }, configurable: true }); return 1; }, configurable: true }); if (a == 1 && a == 2 && a == 3) { document.body.append("Yes, it’s possible."); } })();


Note that the approach of using a getter also works with ===, not just ==.
This solution relies on this being the global object inside the body of the arrow function.
@Midnightas I wouldn't categorize any other answers as "pyramid code".
Note this also works with arbitrary order, doesn't it? Like, (a == 3 && a == 2 && a == 1)?
A
Andrew Bone

Alternatively, you could use a class for it and an instance for the check.

function A() { var value = 0; this.valueOf = function () { return ++value; }; } var a = new A; if (a == 1 && a == 2 && a == 3) { console.log('bingo!'); }

EDIT

Using ES6 classes it would look like this

class A { constructor() { this.value = 0; this.valueOf(); } valueOf() { return this.value++; }; } let a = new A; if (a == 1 && a == 2 && a == 3) { console.log('bingo!'); }


just function A() {value = 0; at the start?
valueOf is being overridden, this method is usually called automatically by JavaScript behind the scenes, and not explicitly in code so when we compare the value it actually increments a..
D
Draco18s no longer trusts SE

I don't see this answer already posted, so I'll throw this one into the mix too. This is similar to Jeff's answer with the half-width Hangul space.

var a = 1; var a = 2; var а = 3; if(a == 1 && a == 2 && а == 3) { console.log("Why hello there!") }

You might notice a slight discrepancy with the second one, but the first and third are identical to the naked eye. All 3 are distinct characters:

a - Latin lower case A
- Full Width Latin lower case A
а - Cyrillic lower case A

The generic term for this is "homoglyphs": different unicode characters that look the same. Typically hard to get three that are utterly indistinguishable, but in some cases you can get lucky. A, Α, А, and Ꭺ would work better (Latin-A, Greek Alpha, Cyrillic-A, and Cherokee-A respectively; unfortunately the Greek and Cherokee lower-case letters are too different from the Latin a: α,, and so doesn't help with the above snippet).

There's an entire class of Homoglyph Attacks out there, most commonly in fake domain names (eg. wikipediа.org (Cyrillic) vs wikipedia.org (Latin)), but it can show up in code as well; typically referred to as being underhanded (as mentioned in a comment, [underhanded] questions are now off-topic on PPCG, but used to be a type of challenge where these sorts of things would show up). I used this website to find the homoglyphs used for this answer.


"Slight discrepancy" is not how I would call that.
@hvd Entirely depends on your font rendering. This is what I see.
@Jake Yeah, the Full Width Latin lower case A isn't the greatest homoglyph (but the capital-letter variants are amazing). Generally though you only need two to get the desired effect.
You can also use unicode variant selector (U+FE00..U+FE0F). None of these are a: a︀ a︁ a︂. No more worrying about discrepancies.
P
Peter Mortensen

Yes, it is possible! 😎

» JavaScript

if‌=()=>!0; var a = 9; if‌(a==1 && a== 2 && a==3) { document.write("

Yes, it is possible!😎

") }

The above code is a short version (thanks to @Forivin for its note in comments) and the following code is original:

var a = 9; if‌(a==1 && a== 2 && a==3) { //console.log("Yes, it is possible!😎") document.write("

Yes, it is possible!😎

") } //-------------------------------------------- function if‌(){return true;}

If you just see top side of my code and run it you say WOW, how? So I think it is enough to say Yes, it is possible to someone that said to you: Nothing is impossible Trick: I used a hidden character after if to make a function that its name is similar to if. In JavaScript we can not override keywords so I forced to use this way. It is a fake if, but it works for you in this case!

» C#

Also I wrote a C# version (with increase property value technic):

static int _a;
public static int a => ++_a;

public static void Main()
{
    if(a==1 && a==2 && a==3)
    {
        Console.WriteLine("Yes, it is possible!😎");
    }
}

Live Demo


The javascript version is a true crime against humanity and the ability to do this, should be illegal by UN-conventions. I think it's about time we purge the world of all knowledge of javacript.
The function declaration could be even shorter. if‌=()=>!0
Why on earth did you use document.write? That's a surefire way not to get hired regardless of the rest of the answer.
@Cerbrus, Thank you for your note. I wrote first my answer with console.log but I changed it to document.write. Really always I use console.log in my codes but here I just want show a text to users in StackOverflow code snippet box. So I wanted to show my message more beautiful than the message generated by console.log. Click the Run Code Snippet button on my answer and on other answers. The SO Code Snippet let me to use html and JS and CSS then I wanted to use it in my answer and make it nice. I think It has not any negative side effect and did not made my answer large or complected.
@Clearer, If UN-Conventions could change the world effectively, then we should have a better world than this. We need somethings more than statement in UN and until that day I think we can use this Javascript trick of mine ;)
E
Eric Duminil

JavaScript

a == a +1

In JavaScript, there are no integers but only Numbers, which are implemented as double precision floating point numbers.

It means that if a Number a is large enough, it can be considered equal to three consecutive integers:

a = 100000000000000000 if (a == a+1 && a == a+2 && a == a+3){ console.log("Precision loss!"); }

True, it's not exactly what the interviewer asked (it doesn't work with a=0), but it doesn't involve any trick with hidden functions or operator overloading.

Other languages

For reference, there are a==1 && a==2 && a==3 solutions in Ruby and Python. With a slight modification, it's also possible in Java.

Ruby

With a custom ==:

class A
  def ==(o)
    true
  end
end

a = A.new

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Or an increasing a:

def a
  @a ||= 0
  @a += 1
end

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Python

You can either define == for a new class:

class A:
    def __eq__(self, who_cares):
        return True
a = A()

if a == 1 and a == 2 and a == 3:
    print("Don't do that!")

or, if you're feeling adventurous, redefine the values of integers:

import ctypes

def deref(addr, typ):
    return ctypes.cast(addr, ctypes.POINTER(typ))

deref(id(2), ctypes.c_int)[6] = 1
deref(id(3), ctypes.c_int)[6] = 1
deref(id(4), ctypes.c_int)[6] = 1

print(1 == 2 == 3 == 4)
# True

It might segfault, depending on your system/interpreter.

The python console crashes with the above code, because 2 or 3 are probably used in the background. It works fine if you use less-common integers:

>>> import ctypes
>>> 
>>> def deref(addr, typ):
...     return ctypes.cast(addr, ctypes.POINTER(typ))
... 
>>> deref(id(12), ctypes.c_int)[6] = 11
>>> deref(id(13), ctypes.c_int)[6] = 11
>>> deref(id(14), ctypes.c_int)[6] = 11
>>> 
>>> print(11 == 12 == 13 == 14)
True

Java

It's possible to modify Java Integer cache:

package stackoverflow;

import java.lang.reflect.Field;

public class IntegerMess
{
    public static void main(String[] args) throws Exception {
        Field valueField = Integer.class.getDeclaredField("value");
        valueField.setAccessible(true);
        valueField.setInt(1, valueField.getInt(42));
        valueField.setInt(2, valueField.getInt(42));
        valueField.setInt(3, valueField.getInt(42));
        valueField.setAccessible(false);

        Integer a = 42;

        if (a.equals(1) && a.equals(2) && a.equals(3)) {
            System.out.println("Bad idea.");
        }
    }
}

@cᴏʟᴅsᴘᴇᴇᴅ: Java, Javascript, potayto, potahto :) There are enough good JS answers already. I just thought it would be interesting to show how it can be done in other languages, and possibly give JS developers some ideas.
@cᴏʟᴅsᴘᴇᴇᴅ: Updated with a JS example.
Why doesn't the Java version work with Integer a = 42 (or does it)? As I understand autoboxing, Integer a = 42; a == 1 && a == 2 && a == 3 should box all of the ints. Or does this unbox a for the comparisons?
@CAD97: Integer == int seems to result in unboxing. But using Integer#equals(int) forces autoboxing, so it works. Thanks for the comment!
@StephanBijzitter: Please explain. As far as I know, there are only Numbers in JS, which are basically like doubles. They can look like integers and you can use them like integers, but they still aren't integers. I don't think n == n + 1 can ever be true for integers in Java/Python/C/Ruby/...
S
Salman A

This is an inverted version of @Jeff's answer* where a hidden character (U+115F, U+1160 or U+3164) is used to create variables that look like 1, 2 and 3.

var a = 1; var ᅠ1 = a; var ᅠ2 = a; var ᅠ3 = a; console.log( a ==ᅠ1 && a ==ᅠ2 && a ==ᅠ3 );

* That answer can be simplified by using zero width non-joiner (U+200C) and zero width joiner (U+200D). Both of these characters are allowed inside identifiers but not at the beginning:

var a = 1; var a‌ = 2; var a‍ = 3; console.log(a == 1 && a‌ == 2 && a‍ == 3); /**** var a = 1; var a\u200c = 2; var a\u200d = 3; console.log(a == 1 && a\u200c == 2 && a\u200d == 3); ****/

Other tricks are possible using the same idea e.g. by using Unicode variation selectors to create variables that look exactly alike (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).


M
MonkeyZeus

Rule number one of interviews; never say impossible.

No need for hidden character trickery.

window.__defineGetter__( 'a', function(){ if( typeof i !== 'number' ){ // define i in the global namespace so that it's not lost after this function runs i = 0; } return ++i; }); if( a == 1 && a == 2 && a == 3 ){ console.log( 'Oh dear, what have we done?' ); }


Ouch. __defineGetter__ is actually not part of the js language, just an ugly version of defineProperty. typeof is not a function and this undeclared i is just awful. Still seems to be worth 40 upvotes :/
@JonasW. 41 upvotes :-) I am aware that __defineGetter__ is deprecated per developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… but it clearly executes in my FireFox v 57.0.4 so I opted to show this instead of defineProperty() because legacy code is real and cannot be ignored. Regardless of the ugliness, declaring i in the way I did is a well known/documented behavior. Maybe I was just in a PCG mood ¯\_(ツ)_/¯
F
Frank W. Zammetti

Honestly though, whether there is a way for it to evaluate to true or not (and as others have shown, there are multiple ways), the answer I'd be looking for, speaking as someone who has conducted hundreds of interviews, would be something along the lines of:

"Well, maybe yes under some weird set of circumstances that aren't immediately obvious to me... but if I encountered this in real code then I would use common debugging techniques to figure out how and why it was doing what it was doing and then immediately refactor the code to avoid that situation... but more importantly: I would absolutely NEVER write that code in the first place because that is the very definition of convoluted code, and I strive to never write convoluted code".

I guess some interviewers would take offense to having what is obviously meant to be a very tricky question called out, but I don't mind developers who have an opinion, especially when they can back it up with reasoned thought and can dovetail my question into a meaningful statement about themselves.


The question (or all interview questions) is probably to test the candidates willingness to think about a problem, especially ones that are "apparently obvious", like this one. Someone who refuses to think because they believe they "know" the answer is not a good hire.
@Don Hatch No, I wouldn't penalize them if they answered in good faith and especially if they gave a correct answer like those others have shown... but I would then ask a follow-up to try and probe whether they think it's a good way to write code or not. Being knowledgeable and being able to come up with a "correct" answer is only part of being a good developer. Far more important for a "professional" developer is writing code that is understandable and maintainable down the road, often times by less capable developers. Overly clever developers are pretty much as bad as incapable ones IME.
This doesn't answer the question.
The sad thing about this answer is that a 1rep user answered that yesterday and got 2 downvotes causing him to delete this question.
@JohnColeman the question asks how the code could evaluate to true. It doesn't ask the reasons the interviewer proposed the question in the first place. This answer doesn't even attempt to address the question being asked, and instead focuses entirely on a "what I would do" version of an attempt at guessing what the interviewer's purpose was. If that were the question asked, it would be way too broad. Therefore this answer doesn't belong here or anywhere on the site.
D
Dirk Vollmar

If you ever get such an interview question (or notice some equally unexpected behavior in your code) think about what kind of things could possibly cause a behavior that looks impossible at first glance:

Encoding: In this case the variable you are looking at is not the one you think it is. This can happen if you intentionally mess around with Unicode using homoglyphs or space characters to make the name of a variable look like another one, but encoding issues can also be introduced accidentally, e.g. when copying & pasting code from the Web that contains unexpected Unicode code points (e.g. because a content management system did some "auto-formatting" such as replacing fl with Unicode 'LATIN SMALL LIGATURE FL' (U+FB02)). Race conditions: A race-condition might occur, i.e. a situation where code is not executing in the sequence expected by the developer. Race conditions often happen in multi-threaded code, but multiple threads are not a requirement for race conditions to be possible – asynchronicity is sufficient (and don't get confused, async does not mean multiple threads are used under the hood). Note that therefore JavaScript is also not free from race conditions just because it is single-threaded. See here for a simple single-threaded – but async – example. In the context of an single statement the race condition however would be rather hard to hit in JavaScript. JavaScript with web workers is a bit different, as you can have multiple threads. @mehulmpt has shown us a great proof-of-concept using web workers. Side-effects: A side-effect of the equality comparison operation (which doesn't have to be as obvious as in the examples here, often side-effects are very subtle).

These kind of issues can appear in many programming languages, not only JavaScript, so we aren't seeing one of the classical JavaScript WTFs here1.

Of course, the interview question and the samples here all look very contrived. But they are a good reminder that:

Side-effects can get really nasty and that a well-designed program should be free from unwanted side-effects.

Multi-threading and mutable state can be problematic.

Not doing character encoding and string processing right can lead to nasty bugs.

1 For example, you can find an example in a totally different programming language (C#) exhibiting a side-effect (an obvious one) here.


Then, the question becomes way too broad. Different languages can implement this with varying degrees of ease. The question has gained so much traction because it is a JS specific Q&A, but that's just my 2c.
the causes are different C# and javascript so this answer is not legit.
@Edwin: The causes are exactly the same: Unicode fiddling with similar-looking glyphs or space characters, race conditions, or side-effects of the comparison operation (the latter being shown in my example).
@cᴏʟᴅsᴘᴇᴇᴅ: Sometimes looking at things from a broader angle helps see the actual problem.
I wish this answer could be tagged to this question in some "meta" way. After reading all of the answers above it, I was left feeling like JS has so many holes, but you just summed up all of the answers in one go. And you did it in a way that makes this into a stellar interview question (if the language-specific tag is removed) in my opinion. Bravo!
T
Théophile

Here's another variation, using an array to pop off whatever values you want.

const a = { n: [3,2,1], toString: function () { return a.n.pop(); } } if(a == 1 && a == 2 && a == 3) { console.log('Yes'); }


D
Durga

Okay, another hack with generators:

const value = function* () { let i = 0; while(true) yield ++i; }(); Object.defineProperty(this, 'a', { get() { return value.next().value; } }); if (a === 1 && a === 2 && a === 3) { console.log('yo!'); }


You say hack, but I'm pretty sure this is the use case of generators... :) (well, except that this relies on this being the window object)
P
Peter Mortensen

Using Proxies:

var a = new Proxy({ i: 0 }, {
    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3);

Proxies basically pretend to be a target object (the first parameter), but intercept operations on the target object (in this case the "get property" operation) so that there is an opportunity to do something other than the default object behavior. In this case the "get property" action is called on a when == coerces its type in order to compare it to each number. This happens:

We create a target object, { i: 0 }, where the i property is our counter We create a Proxy for the target object and assign it to a For each a == comparison, a's type is coerced to a primitive value This type coercion results in calling a[Symbol.toPrimitive]() internally The Proxy intercepts getting the a[Symbol.toPrimitive] function using the "get handler" The Proxy's "get handler" checks that the property being gotten is Symbol.toPrimitive, in which case it increments and then returns the counter from the target object: ++target.i. If a different property is being retrieved, we just fall back to returning the default property value, target[name]

So:

var a = ...; // a.valueOf == target.i == 0
a == 1 && // a == ++target.i == 1
a == 2 && // a == ++target.i == 2
a == 3    // a == ++target.i == 3

As with most of the other answers, this only works with a loose equality check (==), because strict equality checks (===) do not do type coercion that the Proxy can intercept.


There isn’t a point in using a proxy for this, though – defining Symbol.toPrimitive in the same way on an object would work just as well.
T
Tim

Actually the answer to the first part of the question is "Yes" in every programming language. For example, this is in the case of C/C++:

#define a   (b++)
int b = 1;
if (a ==1 && a== 2 && a==3) {
    std::cout << "Yes, it's possible!" << std::endl;
} else {
    std::cout << "it's impossible!" << std::endl;
}

I don't think it's possible in every programming language. Not all languages have preprocessors, for example. For that matter, not all languages use && for logical "and".
I found a way that works both in Python and C++ which uses operator overloading.
And you can do it in Java by using reflection and messing up the integer cache.
Can't do it in languages that woudn't support mutation in that spot, e.g. nothing comparable is available in haskell
The question is asking about JavaScript, not C++.
P
Preda7or

Same, but different, but still same (can be "tested" multiple times):

const a = { valueOf: () => this.n = (this.n || 0) % 3 + 1} if(a == 1 && a == 2 && a == 3) { console.log('Hello World!'); } if(a == 1 && a == 2 && a == 3) { console.log('Hello World!'); }

My idea started from how Number object type equation works.


P
Peter Mortensen

An ECMAScript 6 answer that makes use of Symbols:

const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));

Due to == usage, JavaScript is supposed to coerce a into something close to the second operand (1, 2, 3 in this case). But before JavaScript tries to figure coercing on its own, it tries to call Symbol.toPrimitive. If you provide Symbol.toPrimitive JavaScript would use the value your function returns. If not, JavaScript would call valueOf.


g
gafi

I think this is the minimal code to implement it:

i=0,a={valueOf:()=>++i} if (a == 1 && a == 2 && a == 3) { console.log('Mind === Blown'); }

Creating a dummy object with a custom valueOf that increments a global variable i on each call. 23 characters!


B
Ben Aubin

This one uses the defineProperty with a nice side-effect causing global variable!

var _a = 1 Object.defineProperty(this, "a", { "get": () => { return _a++; }, configurable: true }); console.log(a) console.log(a) console.log(a)


you could use a closure over a: get: (a => () => ++a)(0), no global necessary.
@NinaScholz sure, but we're talking about bad practices here - just let me have this :D
J
Jonathan Kuhl

By overriding valueOf in a class declaration, it can be done:

class Thing {
    constructor() {
        this.value = 1;
    }

    valueOf() {
        return this.value++;
    }
}

const a = new Thing();

if(a == 1 && a == 2 && a == 3) {
    console.log(a);
}

What happens is that valueOf is called in each comparison operator. On the first one, a will equal 1, on the second, a will equal 2, and so on and so forth, because each time valueOf is called, the value of a is incremented.

Therefore the console.log will fire and output (in my terminal anyways) Thing: { value: 4}, indicating the conditional was true.


N
Nguyễn Văn Phong

As we already know that the secret of loose equality operator (==) will try to convert both values to a common type. As a result, some functions will be invoked.

ToPrimitive(A) attempts to convert its object argument to a primitive value, by invoking varying sequences of A.toString and A.valueOf methods on A.

So as other answers using Symbol.toPrimitive, .toString, .valueOf from integer. I would suggest the solution using an array with Array.pop like this.

let a = { array: [3, 2, 1], toString: () => a.array.pop() }; if(a == 1 && a == 2 && a == 3) { console.log('Hello World!'); }

In this way, we can work with text like this

let a = { array: ["World", "Hello"], toString: () => a.array.pop() }; if(a == "Hello" && a == "World") { console.log('Hello World!'); }