◆ stackoverflow のクイズ
◆ 色々方法あって楽しめた

今週話題になってたらしい クイズ?
JavaScript で (a ==1 && a== 2 && a==3) を true にする方法

暇つぶしに はてなブックマークのホットエントリー を眺めていたら見つけたページで stackoverflow のページがありました
https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true

valueOf/toString

(a ==1 && a== 2 && a==3) を評価して true にすればいいようです
普通に考えたら できなそうですが JavaScript だと toString とか valueOf とかで関数実行できるし 簡単にいけるんじゃない?と思って回答を見てみると当たりでした
ただ valueOf と toString どっちだったか自信なくて
両方かけば動くだろうけど~ 本命は~ たぶん valueOf ? と言う感じでギリギリ感あります


答えは 片方だけならどっちでも動きます

var _a = 1
var a = {
valueOf(){return _a++}
}
console.log(a == 1 && a == 2 && a == 3)
// true

var _a = 1
var a = {
toString(){return _a++}
}
console.log(a == 1 && a == 2 && a == 3)
// true

両方つけた場合は valueOf が優先されます
var obj = {
valueOf(){
console.log("valueOf")
return 1
},
toString(){
console.log("toString")
return "1"
}
}
console.log(obj == "1")
// valueOf
// true

両方あって valueOf がオブジェクトだとさらに toString も呼ばれます
var obj = {
valueOf(){
console.log("valueOf")
return {}
},
toString(){
console.log("toString")
return "1"
}
}
console.log(obj == "1")
// valueOf
// toString
// true

変数を 3 つ用意する

他にも a の前後のスペースに統一性がないので 変数名にできる空白をつけて 3 つの異なる変数を作っていたり 面白い回答もあって楽しめました

var a_ = 1
var a = 2
var _a = 3

↑の _ のところが変数にできる空白文字
ハングルらしいです

問題が (a ==1 && a== 2 && a==3) とわざとなのか偶然なのか 1 つめの a のあとだけスペースがあるからこそですね

一応スペースがなくても JavaScript の変数名には ZWNJ が使えるので見えない空白文字があると仮定して 3 つの変数を用意することもできます
ZWNJ というのは ZeroWidthNonJoiner という幅のない見えない文字です

コードにすると
var code = `
var a = 1
var a\u200c = 2
var a\u200c\u200c = 3
console.log(a == 1 && a\u200c == 2 && a\u200c\u200c == 3)
`
eval(code)
// true

\u200c が ZWNJ です
わかりやすく \u200c と書いていますが code を出力すると

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

となって ZWNJ があるかどうかは見分けがつきません

getter

また あまり使う機会がないので全く思いつかなかったのですが a プロパティに getter 付けたオブジェクトを with する方法もありました

var _a = 1
with ({get a(){return _a++}}){
console.log(a == 1 && a == 2 && a == 3)
}
// true

ただ グローバルと仮定すれば別回答にある window のプロパティ a に getter を付けるだけでもできます
こっちだと strict モードで with が禁止されていても大丈夫ですね

var _a = 1
Object.defineProperty(window, "a", {
get(){
return _a++
}
})
console.log(a == 1 && a == 2 && a == 3)
// true

変数を 3 つ用意する方法や getter の方法だと 同じ型にできるので == でなく === でも大丈夫です

valueOf 2

valueOf を使う方法は他にもあって 配列の valueOf では join メソッドが呼び出されることを利用して join プロパティを shift に置き換えて自身の値を更新します

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

一見 valueOf も toString も書き換えてないのでどうなってるのだろうと不思議に思える方法です



JavaScript は色々書き方があっておもしろい言語ですよね

案の定コメントにはダメな言語とか書いてる人がいますが 出来る人ほどこういう裏技っぽいものに興味を持って利用して何か作ってみようとしてると思います
言語をダメと言ってるようで実際のところは自分には使いこなせないです 扱いきれませんと言ってるようなものですしね

toString/valueOf は普通に使う分にはあまり触れることないところですが変なもの作ってみようってときにはいじってみるといいと思います
http://2ality.com/2011/12/fake-operator-overloading.html
こんなこともできたりね

ただ 実際に役立つ使い方 というと
== じゃなくて === でも使えて (== は特別理由ないときはできるだけ使わない方がいい)
strict mode でも使える (将来的に module は強制 strict だから)

defineProperty?

なんか面白くない普通なものになりました


逆に使うと一番後悔するのは見えない変数文字でしょうね
まだ空白があるハングルのだといいですが ZWNJ なんか使い始めると もうどこにあるのかわからないですから

しかもコピペしたときにエディタによっては消されて なぜか動かなくなることもあったりで……


昔は CodeIQ (だっけ?) でやってたこんな感じの JavaScript クイズが好きでよくやってましたが久しぶりにやってみたくなりました