Get #Amazon #Prime for this #holiday #amazonprime #christmas #2019

#JavaScript: (a == 1 && a == 2 && a == 3) == true?

I came across this highly rated can-a-1-a-2-a-3-ever-evaluate-to-true stackoverflow post about a month ago. It really opened my eyes on the number of possible solutions. Thus, I would like to share with you.

StackOverflow Question
StackOverflow Question

Question

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

Possible Solutions

1) Using Loose equality == and Object's prototype method valueOf or toString


const a = {
  value: 1,
  valueOf: function () {
    return a.value++;
  }
}

OR 

const a = {
  value: 1,
  toString: function () {
    return a.value++;
  }
}


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

How does this work?
When a (an object) is being compared to 1 (a number) on the right, JavaScript engine will try to convert the type of the operand(due to loose equality) so that they are comparable.  First, valueOf will be called, if it fails, toString will be called. When the type of both operands are the same, it will do equality check.

In this case, whenever an equality check is done, a.value is being increased and thus resulting in a == 1, a == 2, a == 3 and so on.

Another variations of defining valueOf


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

var a = new A();

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

ES6 class
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('It works!');
}

Symbol.toPrimitive
From MDN: It is a symbol that specifies a function valued property that is called to convert an object to a corresponding primitive value.

let a = {
  [Symbol.toPrimitive]: ((i) => () => ++i) (0)
};

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

An example of Symbol.toPrimitive() taken from MDN
// An object with Symbol.toPrimitive property.
var obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint == 'number') {
      return 10;
    }
    if (hint == 'string') {
      return 'hello';
    }
    return true;
  }
};
console.log(+obj2);     // 10        -- hint is "number"
console.log(`${obj2}`); // "hello"   -- hint is "string"
console.log(obj2 + ''); // "true"    -- hint is "default"

2) Using pop with an array and Object's prototype method valueOf
Array.prototype.pop() removes and returns the last item from the array.

const a = {
  arr: [3,2,1],
  toString: function () {
    return a.arr.pop();
  }
}

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

3) Using Loose equality == and Overriding array join function with array shift function
In this case, when a is being compared, it will be converted to a string which is done via join. However, since join had been overrode by shift. It essentially removes and returns the first element in the array.

a = [1,2,3];
a.join = a.shift;

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

4)  Object.defineProperty with an accessor descriptor

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

Using with which is not recommended. with states the main object / scope of the enclosing statements

var value = 0;

with({
  get a() {
    return ++value;
  }
}) {
  if (a == 1 && a == 2 && a == 3)
    console.log('It works!');
}

getter function of 'a' increases its value everytime its value is retrieved.

5) Using self-overwriting getter functions
In this case, it is essentially reset / overwrite its getter function the first three times variable 'a' is called.

(() => {
    '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('It works!');
    }
})();

6) Halfwidth and fullwidth forms:abc
Taken from Wikipedia:
In CJK (Chinese, Japanese and Korean) computing, graphic characters are traditionally classed into fullwidth and halfwidth characters. With fixed-width fonts, a halfwidth character occupies half the width of a fullwidth character, hence the name.


This is essentially saying these 3 looking alike 'a' variables are technically 3 different characters with space before, after and no space around the character 'a'. 

var a_ = 1;
var a = 2;
var _a = 3;
if (a_==1 && a== 2 &&_a==3) {
    console.log('It works!')
}

You can use this string validator by Mathias to validate if a string is valid in JavaScript

7) Zero-width joiner
In this case, they are essentially visually alike characters but they contain zero-width character(s) which are invisible.

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('It works!')
}

Check out this StackOverflow thread for another example

var ab, /* ab */
    a‍b, /* a‍b */
    a‌b; /* a‌b */

8) Unicode Variant Selectors / homoglyphs
Taken from Wikipedia:
Variation Selectors is a Unicode block containing 16 Variation Selector format characters (designated VS1 through VS16). They are used to specify a specific glyph variant for a Unicode character.
A set of unicode character variations is essentially  a set of looking very similar visually characters.

9) Using Regex with global flag

This is quite tricky as it works because of global flag. Every time a match is found, the lastIndex of the regex match is updated. This causes the next search steps forward one digit after each match. 
The first match is this.r.lastIndex == 0
var a = {
  r: /\d/g,
  valueOf: function(){
    return this.r.exec(123)[0]
  }
}

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

10) Using a generator
Taken from MDN:
Generator: allows us to define an iterative algorithm by writing a single function which can maintain its own state.

const value = function* () {
  let i = 0;
  while(true) yield ++i;
}();

Object.defineProperty(window, 'a', {
  get() {
    return value.next().value;
  }
});

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

11) Multi-threading possible bug
The author mentioned that on one of his attempts, it happened after 10 billion iterations.
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
    })
})

Summary

There was this answer that summarized the mentioned solutions into
i) Encoding issue
ii) Multi-threading issue - race condition (using web workers) which was this answer
iii) Side-effects of equality comparison operation

Thank you for reading!

Jun