a == 1 && a == 2 && a == 3を成立させる

# 初めに
この記事は前回の記事の[ゼロ幅を利用した難読化](https://qiita.com/ikesou/items/033c4afbdec3c4ea0323)の小ネタの内容です。


javascriptの == を利用した比較は以下のとおりに評価が行われます。
この評価方法を利用して条件を成立させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305681/fef4c3b8-df68-9da2-10c3-5cf0dd6b486e.png)


# 成立させる方法
### 1. ToPrimitiveの動作を利用
```js
var a = {
  value : 1,
  valueOf: function () {
    return this.value++;
  }
};
if (a == 1 && a == 2 && a == 3) {
  console.log('ok');
} else {
  console.log('no');
}
// ok

```
　9. にあるように、オブジェクトと数字(または文字列)の比較、つまりa(x)と1,2,3(y)を比較するとToPrimitive(x) == yの結果が返ります。
ToPrimitiveは名前通りプリミティブに変換する関数です。
> JavaScript において、プリミティブ (primitive、プリミティブ値、プリミティブデータ型) はオブジェクトでなく、メソッドを持たないデータのことです。 6 種類のプリミティブデータ型があります。文字列、数値、BigInt、真偽値、undefined、そしてシンボル (ECMAScript 2016 で追加) です。

詳しくはこちらをどうぞ: [Primitive (プリミティブ) / MDN Web Docs](https://developer.mozilla.org/ja/docs/Glossary/Primitive)

このToPrimitiveの処理は以下の通りになっています。
1. [Symbol.toPrimitive]（hint） メソッドがあればそれを呼び出す
2. なければhintが
　2-1. "string"ならば toString() -> valueOf()の順に優先される
　2-2. それ以外ならば　valueOf() -> toString()の順に優先される

hintは"string", "number", "default"のいずれかであり、その値は以下のように選ばれます。

```js
const obj = {
  [Symbol.toPrimitive](hint) {
    console.log(hint);
  }
}

// 数字が返されることを期待されるときはnumber
console.log(+obj); 
// number 
// NaN
console.log(0 < obj);
// number
// false

// 文字列が返されることを期待されるときはstring
alert(obj);
// string
// undefined

// それ以外のときはdefault
console.log(0 == obj);
// default
// false

```

細かい話は以下のサイトをご確認ください。
・[Symbol.toPrimitive  / MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive)
・[ECMAScript® 2022 Language Specification](https://tc39.es/ecma262/#sec-toprimitive)

つまり、今回は2-2の順番で優先されます。
※ **obj** のvalueOf() -> toString()の順に優先されるため、以下のコードのようにtoString()だけを定義してもObjectのメソッドであるvalueOf()は利用されません。

```js
var a = {
  value : 1,
  toString: function () {
    return this.value++;
  }
};
if (a == 1 && a == 2 && a == 3) {
  console.log('ok');
} else {
  console.log('no');
}
// ok
```

どちらも定義していれば以下のように動作します。

```js
var a = {
  value : 1,
  valueOf: function() {
    console.log('valueOf', this.value);
    return this.value++;
  },
  toString: function () {
    console.log('toString', this.value);
    return this.value++;
  }
};
if (a == 1 && a == 2 && a == 3) {
  console.log('ok');
} else {
  console.log('no');
}
// valueOf 1
// valueOf 2
// valueOf 3
// ok
```

### 2. ArrayのtoString()の動作を利用
```js
var a = [1,2,3];
a.join = a.shift;

if (a == 1 && a == 2 && a == 3) {
  console.log('ok');
} else {
  console.log('no');
}
// ok
```

こちらは配列のtoString()の動作を利用した方法です。
1. で説明したように、オブジェクトと数字の比較をするとtoString() or valueOf()が実行されます。
そこで、配列のtoString()の動作を利用します。
toString()が実行されると以下の画像にあるようにjoinが呼ばれます。つまり、joinをshiftにオーバーライドすることで == の比較が行われるたびshiftが実行されるということです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305681/6783d38a-3551-bb2d-5f9a-86aa683107fa.png)
[ECMAScript® 2022 Language Specification](https://tc39.es/ecma262/#sec-array.prototype.tostring)


### 3. RegExpを利用
```js
var a = {
  value: /\d/g, 
  valueOf: function(){
    return this.value.exec(123)[0];
  }
}

if (a == 1 && a == 2 && a == 3) {
  console.log('ok');
} else {
  console.log('no');
}
```
こちらも1. と同様にToPrimitive()の動作を利用したものです。

javascriptのRegExpの静的プロパティにはlastIndexがあり、gフラグのある正規表現でtestやexecを実行したときに次の一致を開始する位置が格納されます。
つまり、exec()を実行すると123の1にマッチし、lastIndexには次の開始位置である1が格納されます。そのため次のマッチは2になるということです。

# 終わりに
今回のようなことは通常のコーディングでは起こりにくいと思いますが、比較を行う際は == ではなく === を利用しましょう。

# 参考
[Can (a== 1 && a ==2 && a==3) ever evaluate to true? / Stack overflow
](https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true)