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

This is interview question asked by a major tech company. My answer was that it's impossible. They said nothing is impossible. It happened 2 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.

share|improve this question
4  
Comments are not for extended discussion; this conversation has been moved to chat. – deceze 19 hours ago
5  
insert obligatory joke about how this would "only work in javascript". oops my bad. the question is already marked as "javascript". – Trevor Boyd Smith 9 hours ago
13  
If this is somewhat representative of the kind of code that dwells in that company's codebase … run 💨🏃‍♂️ – FeifanZ 7 hours ago
8  
In response to the "too broad" close votes, there are really only two categories of answers: namely, (1) a custom toString/valueOf, and (2) a getter, either on the global object or in a with. Considering that that is the list of answers that completely resolve the question, I respectfully submit that this question isn't too broad. – apsillers 7 hours ago
48  
This is simply awfull interview question... – ghord 7 hours ago

17 Answers 17

up vote 765 down vote accepted

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 loose equality operator used. 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.

share|improve this answer
8  
Could you achieve this by altering the implied valueOf() operation? – Sterling Archer yesterday
5  
Yes, valueOf works in place of toString for the same reason – Kevin B yesterday
2  
Comments are not for extended discussion; this conversation has been moved to chat. – deceze 19 hours ago
4  
According to this a number conversion will be tried first so valueOf is slightly better. – Salman A 15 hours ago
4  
@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. ;) – tomsmeding 15 hours ago

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.

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.

share|improve this answer
68  
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 21 hours ago
2  
@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. – Jeff 20 hours ago
4  
wow, that's fascinating! @Jeff, your answer made my day, and probably the rest of the week. Even after reading the comments it took me some time to realize there are three completely different variables, one with a weird character after a, one with it before a and the last one with just a. – margaretkru 20 hours ago
15  
Of course, this is banned as a standard loophole, which also applies to interviews. [citation needed] – Sanchises 15 hours ago
4  
Could someone please write an uglifier which uses those space characters for variables. – schmunk 11 hours ago

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.

share|improve this answer
14  
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 yesterday
1  
@Pointy - And, I program in strict mode where with is not allowed. – jfriend00 yesterday
1  
@Pointy in the accepted answer they do something similar without the with so it can happen – Jorrit 16 hours ago
1  
@jorrit no one would use ==. And === prevents the accepted answer – Jonas W. 15 hours ago
1  
@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. – wortwart 8 hours ago

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;
    }
  }
}

share|improve this answer
4  
I am up-voting this answer for its' creativity. Thank you! – Ivan Venediktov 16 hours ago
2  
My high score is after 5 trials! – George Thomas 16 hours ago
1  
I clicked a few times and got 1 and 289 as my extremes! – user3070485 16 hours ago
2  
Horray for --bogosort-- bogogeneration? – Baldrickk 11 hours ago
2  

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.

share|improve this answer
    
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 yesterday
    
@jfriend00 you mean if you placed var a; somewhere? – jontro yesterday
    
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 yesterday
    
@jfriend00 well sure. Not sure that it would add much more value in combination with the other already answers. Will update the answer – jontro yesterday

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.");
	}
})();

share|improve this answer
11  
Note that the approach of using a getter also works with ===, not just ==. – Makyen 11 hours ago

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("!")
}

share|improve this answer
2  
Umm.. how does this works? – Abdillah 2 hours ago
2  
@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. – Simon Chan 1 hour ago
    
I see, so the this.r regex object remember the state / index. Thanks! – Abdillah 1 hour ago

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  = 2;
var а = 3;
if(a == 1 &&  == 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 PCCG, 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.

share|improve this answer
1  
"Slight discrepancy" is not how I would call that. – hvd 3 hours ago
    
@hvd Entirely depends on your font rendering. This is what I see. – Draco18s 3 hours ago
    
    
@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. – Draco18s 2 hours ago
    
@Draco18s Agreed re: only 2 usually being needed. Good job on the extra info too! – JakeSteam 2 hours ago

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

function A(value) {
    value = value || 0;
    this.valueOf = function () { return ++value; };
}

var a = new A;

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

share|improve this answer

Rule number one of interviews; never say impossible.

No need for hidden character trickery.

window.__defineGetter__( 'a', function(){
        if(typeof(i) !== 'number'){
            i = 0;
        }
        return ++i;
    });
    
    if( a == 1 && a == 2 && a == 3 ){
        alert('Oh dear, what have we done?');
    }

share|improve this answer
1  
Your alert made me laugh. May I also suggest alert("My god, it's full of vars!") – brichins 7 hours ago

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.

share|improve this answer
    
So, would you penalize someone who takes your question in good faith, and is so knowledgable that they can actually answer it correctly? I'd be pretty upset if someone did that to me. Not sure whether that's your intent, but if it's not, you might want to edit your answer to make it clear. – Don Hatch 2 hours ago
1  
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. – Shammoo 1 hour ago

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'll leave as homework the demonstration for others languages. Have fun.

share|improve this answer
5  
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". – Keith Thompson 8 hours ago
    
I found a way that works both in Python and C++ which uses operator overloading. – Donald Duck 7 hours ago
    
Most languages will allow you to overwrite the operator – LukeS 6 hours ago
1  
Please do the same in Brainf*ck – Hagen von Eitzen 6 hours ago
    
And you can do it in Java by using reflection and messing up the integer cache. – CAD97 4 hours ago

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');
}

share|improve this answer

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!');
}
share|improve this answer

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)

share|improve this answer
2  
you could use a closure over a: get: (a => () => ++a)(0), no global necessary. – Nina Scholz 8 hours ago
1  
@NinaScholz sure, but we're talking about bad practices here - just let me have this :D – Ben Aubin 8 hours ago

Even without confusing naming, overloading, or random variables, a == 1 && a == 2 && a == 3 can return true in multi-threaded environments as the value of a may change between each comparison as long as it is not thread-safe.

share|improve this answer
    
Unless on node.js, JavaScript should be thread safe, as it's singlethreaded – Cristik 7 hours ago
6  
Node.js is actually single-threaded, so nope. – Iso 7 hours ago

if you are allowed to reverse numbers and variables, the trick can be done with valueOf:

var i = 0
var a = {i: 0, valueOf: () => ++ i}
1 == a && 2 == a && 3 == a
// => true

share|improve this answer
    
this is actually not an aswer.. should have been posted to comment. Where so brilliant answers are already available, why to post this. – undefined 9 hours ago
    
@undefined Ok, perhaps a comment and not an answer. But I used a different technique than that brilliant answers. So this post was just to complete the techniques to achive that strange behaviour – afx 6 hours ago
    
Reversing numbers and variables isn't necessary here. a==1 && a==2 && a==3 is true as well. And valueOf was already exploited in the same way in earlier answers (e.g. answer by Nina Scholz) – default locale 42 mins ago

protected by Community 11 hours ago

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.