(a == 1 && a == 2 && a == 3) === true - Wait, hold on...
Tags: javascript, wtfjs, coercion
Published: 2019-11-26 18:44:10

Some of you may recognize the problem in the title. It's a bit of a famous sample of [wtfJS](https://dev.to/emnudge/wtfjs-and-coercion-454e), explained very well by [Brandon Morelli in 2018](https://codeburst.io/javascript-can-a-1-a-2-a-3-ever-evaluate-to-true-aca13ff4462d).

The code sample is as follows:

```js
    if (a == 1 && a == 2 && a == 3) {
    	console.log("What?!");
    }
    // logs: What?!
```

Why does it work? Well the trick is in realizing that `a` here is not a primitive. It's an object with a getter - pretending to be a primitive.

So what happens when we even try to compare an object and a primitive? If we take a look at [the spec](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-abstract-equality-comparison) it says (by rules 8 & 9) that we try to coerce the object into a primitive. How? Via `ToPrimitive`, a function defined in [the spec](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-toprimitive) as well.

In short, it tries to coerce the object into a number. If that doesn't work, it tries to coerce it into a string. Let's try to coerce an object to a string and number.
```js
    const num = Number({});
    console.log(num); // NaN
    
    const str = String({});
    console.log(str); // [object Object]
```

Okay, so neither of those are helpful. How exactly is it coercing them?

According to [the spec](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-ordinarytoprimitive), it's calling `.valueOf` to get the number and `.toString` to get the string. If `.valueOf` returns an object, it moves on to `.toString`. If `.toString` doesn't return a primitive, it will actually throw an error:  `Uncaught TypeError: Cannot convert object to primitive value`. 

We can override them ourselves like so:
```js
    const a = {
    	valueOf()  {
    		return 55;
    	},
    	toString() {
    		return 100;
    	}
    };
    
    if (55 == a) console.log("we got valueOf()!");
    if (100 == a) console.log("we got toString()!");
    // logs: we got valueOf()!
    
    // returning an object, so it will be skipped
    a.valueOf = function() { return {} };
    
    if (55 == a) console.log("we got valueOf()!");
    if (100 == a) console.log("we got toString()!");
    // logs: we got toString()!
```
You see, we don't actually have to return a string or number in either. 

So how do we use this to solve our problem? We make one of the getters return a value and increment it.
```js
    const a = {
    	val: 0,
    	valueOf()  {
    		this.val++;
    		console.log("value incremented!");
    		return this.val;
    	}
    };
    
    if (a == 1 && a == 2 && a == 3) {
    	console.log("We got it!");
    }
    
    // logs:
    // value incremented!
    // value incremented!
    // value incremented!
    // We got it!
```

We can do something similarly with [the `Proxy` class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), but taking advantage of the same concept.
```js
const a = new Proxy({ value: 1 }, {
    get(obj, prop) {
        if (prop !== 'valueOf') return obj[prop];
        return () => obj.value++;
    }
})
```
I won't go too into `Proxy` in this article, as [Keith Cirkel](https://www.keithcirkel.co.uk/) made a much better article on the subject over [here](https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-3-proxies/).

In essence, we are defining a new object with a getter "trap" that returns the current value property and increments it if its `.valueOf()` method is called. This is just a fancier way of doing something that we did much simpler just before.

Regardless, is this impossible using strict equality? What if we were presented with the same example, but with triple equals?

# Strict Equality

Well actually, it's possible. But first, we have to nail down a few fundamentals.

The first being the `window` object. Any property on this object is automatically given to us as if it were defined in some global scope. As such, `window.parseInt` is the same as just `parseInt`, `window.alert` is the same as just `alert`, and so on. 

We can also define our own properties and seemingly create variables dynamically.
```js
    function makeVariables() {
    	window.foo = 55;
    	window.bar = "hello";
    }
    makeVariables()
    
    if (foo) console.log(foo);
    if (bar) console.log(bar);
    if (baz) console.log(baz);
    
    // logs:
    // 55
    // "hello"
    // Uncaught ReferenceError: baz is not defined
```
Side note - this is a bad idea. Don't do this. But we're going to need this for our own example.

Next, we need to go over `Object.defineProperty`. This function lets us define properties on objects with unique qualities. It feels new, but it actually works on IE9. 

This cool method lets us make a property *really* constant, so people don't change it. It also lets us define a custom getter method! Things are starting to feel a bit familiar!
```js
    const myObj = {}
    Object.defineProperty(myObj, 'val', {
    	get() {
    		return Math.random();
        }
    })
    
    console.log(myObj.val);
    console.log(myObj.val);
    console.log(myObj.val);
    
    // logs:
    // 0.6492479252057994
    // 0.6033118630593071
    // 0.6033118630593071
```
Why is this better than the previous method? Well this time we don't have to rely on coercion!

Let's combine the 2 things we just discussed to finalize our second example:
```js
    let value = 0;
    Object.defineProperty(window, 'a', {
    	get() {
    		value++;
    		console.log("value incremented!");
    		return value;
    	}
    })
    
    if (a === 1 && a === 2 && a === 3) {
    	console.log("We got it!");
    }
    
    // logs:
    // value incremented!
    // value incremented!
    // value incremented!
    // We got it!
```
Nice! Now we have it working with strict equality!

We unfortunately can't define a variable in the object itself (and then access it in the getter), but if we really don't want to pollute the scope, we can use closures and IIFEs in a really tricky way (credit goes to P35 from the [SpeakJS](https://discord.gg/RYaesEz) discord server).
```js
    Object.defineProperty(window, 'a', (function(){
    	let value = 0;
    	return {
    		get() {
    			value++;
    			console.log("value incremented!");
    			return value;
    		} 
    	}
    })());
```
but this obviously is a pretty messy example.

What about `Proxy`? Could we use it here? Unfortunately, `Proxy` will not work with the `window` object, so it doesn't help us in this instance.

# Wrap Up

So when is this useful? Hardly ever.

Well, there are some occasions. Have you ever encountered a really weird error when using a JS framework? Something like `Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute`? 

Your framework is probably using Proxies and getter methods under the hood. They're useful, but only when things get complicated and you want to hide that underlying complexity.