JavaScriptの不可解な挙動たち

はじめに

この記事はKogakuin Univ Advent Calendar 2019 18日目の記事です。 adventar.org

 みなさんおはこんばんにちは。どうも、Sugiです。Advent Calenderの記事をかけと言われたものの、書ける && マトモな内容がなかったので、

JavaScriptの不可解な挙動たち

というタイトルで記事を書くことにしました。 18日に…出せるだろうか…

で、どんな事書くのか

 とりあえず、この↓画像をご覧あれ。これは、WebブラウザでF12押すと出てくるアレのConsoleタブでひとつの式を実行した際のスクリーンショットです。

f:id:sgtm0113:20191216152833j:plain
 ブラウザのコンソールにおいて、左端に > と表示された行に式を入力すると、その下の <・ と表示された行に入力した式の返り値が表示されますね。というわけで、このスクリーンショットは、 [[][+[]]+[]][+[]][+[]] の返り値が "u" だということを表しているわけです。

は???[と]と+だけでどうやって文字を生成した??そもそもこの式どうなってんだ??

 ということで、この式がどうなってんのか、そしてこれに類似した挙動、またそれを利用してこんなこともできるよ(やろうと思えばできるよってだけで、何もいいことないけど)っていうのを解説していこうと、おもんまぁーーーす!!思います。

とりあえず、あの式どうなってんだ

先程の式 [[][+[]]+[]][+[]][+[]] を、[ と ] の対応がわかりやすいようにインデントして行を分けると

[
    []
    [
        +[]
    ]
    +[]
]
[
    +[]
]
[
    +[]
]

[ と ] の対応はそれぞれ
1行目 ⇔ 7行目
3行目 ⇔ 5行目
8行目 ⇔ 10行目
11行目 ⇔ 13行目
となっています。

 JavaScriptにおいても多くの言語と同じように、[ と ] で囲まれたものは配列であり…というのは、この記事を見ている人たちには解説不要でしょうから省略。というわけで上のコードを見てみると… [ と ] (そろそろこいつら書くの飽きたんでいい略しかた無いかな)で囲まれた部分は、2~6、9、10行目の3つに別れます。2次元配列?? さて、一つめのブロック、2~6行目に関して見ていきましょう。

[]
[
    +[]
]
+[]

は????
 まぁ分かる人にはわかるんでしょうけども…配列の要素へのアクセスらしく行で書くと、
[][+[]] +[]
うーん…とりあえず、空の配列に添字として +[] をつけてるようです。 +[]とは…?空の配列に+…?そして空の配列の +[] 番目…??? というわけで、2枚目のスクショをどうぞ。

f:id:sgtm0113:20191217002333j:plain
このスクショを見ると、+[]0として評価されることがわかります。
これはどういうことか…JSにおいて、文字列sに符号として+をつけて+sとするとNumber(s)-sだと-Number(s)した値が返ってきます。そして文字列ではない値にこれらの符号を付けた場合、これを文字列に変換したもの (配列の他多くのオブジェクトの場合、.toString()でこれが得られる) がsに入ります。
では、[]を文字列に変換すると? 答えから言うと、""(空の文字列) になります。なぜかといえば、配列を文字列に変換すると、以下のような方法で文字列に変換されるからです。(完全なコードではないです><)

function toString(array){
    var s = "";
    for(var i=0; i < array.length; i++){
        s += array[i];
        if(i < array.length-1){
            s += ",";
        }
    }
    return s;
}

 また、4行目において取り出した配列の各要素が文字列ではない場合、その度に文字列に変換されます。もうお気づきだと思いますが、配列が空の場合はfor文が一度も回らないので、空の文字列が返ってきます。

…えーっと、どこまで解説したっけ? あ、+[][]""になるところまで。つまり、+[]+""と同じであり、これはNumber("")と等価であり、これが0と解釈されるのです。これらを踏まえて、先程の2つめのコードブロックのうち4行目までは、[][0]となります。JSにおいて配列の範囲外参照をするとundefinedが返って来ます。要素数が0の配列の0番目は存在しないので、これもundefinedとなります。では、その後に続く+[]。これはさっきと同じで0だ!…ではなく、それは符号として+-をつけた時の話で、足し算・引き算の場合(記号の前に何か値がある時)はそうでは有りません。じゃあundefined + []はどうなるのよ?それは[]が文字列に変換されて空文字になり、undefined + ""と等価になります。そしてこれは…文字列になり"undefined"になります。
最初のコードブロックの2~4行目が"undefined"になったので、それを置き換えて1行で書くと ["undefined"][+[]][+[]]となります。何度も言ったように+[]は0になるので、["undefined"][0][0]となります。["undefined"][0]で長さ1の配列["undefined"]から0番目の要素を取り出して"undefined"となるので、それに[0]をつけた["undefined"][0][0]"undefined"[0]となり、これは"undefined"の0番目の文字、つまり"u"となるのです。
↑この段落長すぎ…

とりあえず謎は解けたね

 最初に紹介したコード(?)がどうしてuになるのか、これはわかったかと思います。次は、これを応用してこんな事もできるよ!ってのを紹介していきます。

もっとキモいやつ

ごめんなさい今はここまでしかできてません…近日中に出します!