黒魔術（JavaScript）まとめ

# まえがき

JavaScript、書いてますか？
JavaScriptは今や世界中の人々に愛されています。
stackoverflowの2016年の調査によるとJavaScriptは地球上で最も一般的に使用されているプログラミング言語だそうです。

> JavaScript is the most commonly used programming language on earth. Even Back-End developers are more likely to use it than any other language.

![](https://i.gyazo.com/259d9cb3a67cfa3ab749710881e54ea4.png)

[link](http://stackoverflow.com/research/developer-survey-2016)


しかしJavaScriptは愛されすぎているが故、しばしば黒魔術のようだと比喩されることも少なくありません。
愛と憎しみが紙一重とはこのことですね。

ということでそんなこんなはどうでもいいのですが、自分もJavaScriptは大好きです。
今回は黒魔術まとめということで、今までに見かけた奇々怪々なJavaScriptコード達をまとめてみました。

※そのコードから感じられる闇レベルを独断と偏見で定量化してあります。


# 入門編（闇レベル：★☆☆）


## `indexOf`及び`~`演算子

```js
var foods = ['apple', 'banana', 'orange'];

if (~foods.indexOf('apple')) {
    // appleが含まれていた時の処理
}
```

まずは入門編、第一章第一節。
結構よく見かける表現なのではないかと思います。使ったことのある方も多いのではないでしょうか。
リーダブルコード的にはあれだけど、書きやすいし知ってる人同士の間ではとってもリーダブル？な一品。

これは何をしているのかと言うと、`foods`という配列の中に`apple`という要素が含まれているか否かを判定しています。
`.indexOf()`メソッドは配列内に検索対象が見つかればそのインデックスを、見つからなければ`-1`を返すため、下記のように判定されることが多いです。

```js
if (foods.indexOf('apple') !== -1) {
    // appleが含まれていた時の処理
}

// 又は
if (foods.indexOf('apple') > -1) {
    // appleが含まれていた時の処理
}
```

まぁこれでも全く問題ありませんが、なんとなく冗長な感じですね。そこでチルダ演算子の出番です。
チルダ演算子はビット反転演算子と呼ばれます。整数に対してビット反転を適用すると符号を反転させて1を引いた数になります。

よって次のような演算結果を得ることが出来ます。

```js
console.log(~-2); // 1
console.log(~-1); // 0
console.log(~0);  // -1
console.log(~1);  // -2
console.log(~2);  // -3
```

ポイントは`-1`をビット反転させた場合です。`-1`の時だけ`0`になっているのがわかると思います。
JavaScriptでは`0`は`false`、それ以外は`true`なので結果的にこのロジックで判定が可能というわけです。

---

**※追記(2016-10-11)**

@think49 さんより`indexOf()`の挙動に関するコメントを頂きました。

`indexOf()`による配列内検索ですが、`NaN`値の存在を確認したい場合、意図した挙動にならないため注意が必要です。

```js
[NaN].indexOf(NaN); // -1
```

`NaN`値が配列内に含まれているかの確認を行いたい場合は`Array.prototype.includes()`を用いると意図した挙動となります。

```js
[NaN].includes(NaN); // true
```

## `Function Declarations` vs `Function Expressions`

次のコードの内シンタックスエラーになる書き方があります。

```js
// pt1
void function() {
    return 'ok';
}();

// pt2
function() {
    return 'ok';
}();

// pt3
[function() {
    return 'ok';
}()]

// pt4
var hoge = function() {
    return 'ok';
}();
```

正解はpt2です。デバッグコンソールに貼り付けて実行してみると`Uncaught SyntaxError: Unexpected token (`と言われると思います。
pt2はその他のパターンと一見似て見えますが、決定的に違う点が一つだけあります。
それは`Function Declarations（関数宣言）`であるか、`Function Expressions（関数式）`であるかです。

JavaScriptは関数を定義する際、ソースコードが`function`から始まる場合にのみ`Function Declarations`となり、それ以外は全て`Function Expressions`となります。
`Function Declarations`は`}`の後に強制的に`;`が挿入されるため、pt2はJavaScriptエンジンには下記のように解釈されます。

```js
// pt2
function() {
    return 'ok';
};
(); // ← SyntaxError
```

その為、最終行の`();`の部分でSyntaxErrorが起こるというわけです。
このようなコードは即時実行関数と呼ばれ、グローバルスコープ汚染対策として一般的に利用されています。
初めて即時実行関数を見た際、なぜ最初の`function(){}`は括弧で囲う必要があるのか？と思ったものですが、
下記コードの前者がNGで後者がOKな理由は、`式`であるか、`宣言`であるか、の違いによるものだということがわかります。

```js
// NG (SyntaxError)
function() {
    return 'ok';
}();

// OK
(function() {
    return 'ok';
})();
```


# 中級編（闇レベル：★★☆）


## `Array.prototype.slice.call()`

```js
Array.prototype.slice.call(document.querySelectorAll('div')).forEach(function(div) {
    console.log(div);
});
```

なかなかにATフィールドを感じさせるコードですが、これも分かってしまえば便利なヤツです。
通常、`ArrayObject`の要素を一つずつ回したい場合ネイティブに実装されている`forEach()`メソッドを用います。

```js
[1, 2, 3, 4, 5].forEach(function(item) {
    // 処理
});
```

なんの問題も無いですね。`ArrayObject`はをこの`forEach()`メソッドを用いることで要素に一つずつアクセスすることが出来るのです。
`Array.prototype.slice.call().forEach()`なんて魔術を唱える必要はありません。

...が、JavaScriptの世界には`forEach()`で **回すことの出来ない配列** が存在しています。
それらは厳密には`ArrayObject`ではなく、`ArrayLikeObject`と呼ばれます。その名の通り **配列のようなオブジェクト** です。
`ArrayLikeObject`の詳細な解説は割愛しますが、超絶ざっくり説明すると`length`プロパティを持ち、数字添字でアクセス可能な配列のようなオブジェクトが`ArrayLikeObject`として扱われます。

通常`ArrayObject`は次のような処理を通すと`[object Array]`という文字列が得られます。

```js
Object.prototype.toString.call([]); // "[object Array]"
```

しかし`ArrayLikeObject`の場合、`Array`ではないため違う結果を返却されます。

```js
Object.prototype.toString.call(document.querySelectorAll('div')); // "[object NodeList]"
```

これが配列のように見えていても`forEach()`で回すことが出来ない原因です。
`ArrayObject`ではないため、`prototype`に`forEach()`メソッドが継承されていないのです。
そのため`ArrayLikeObject`で`forEach()`を使いたい場合、まず純粋な`ArrayObject`に変換してやる必要があります。
そのための変換処理が`Array.prototype.slice.call()`ということになります。

多少記述が長いと感じる場合、次のように記述することも出来ます。

```js
[].slice.call(arrayLikeObject).forEach(function(item) {
    // 処理
});
```

※参考: [なぜ NodeList は Array ではないのか](https://developer.mozilla.org/ja/docs/Web/API/NodeList#%E3%81%AA%E3%81%9C_NodeList_%E3%81%AF_Array_%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B)

他にも、`document.getElementsByTagName()`が返す、`HTMLCollection`や、関数内で参照すると引数を取得出来る`arguments`等も`ArrayLikeObject`です。


# 上級編（闇レベル：★★★）


## `(0, eval)('this')`

これぞJavaScriptといったところでしょうか。もはや意味がわかりません。
どうやら、かの有名なJavaScriptライブラリ`knockout.js`にて利用されている魔術のようです。

詳細は [こちらの記事](http://qiita.com/SFPGMR/items/7a09507eead5b5db8d17) にて解説されています。

ざっくりな概要ですが、これはJavaScriptの`Indirect eval call`という仕様を利用した、`global`な`window`オブジェクトを取得するためのテクニックです。
間接的に`eval`を実行することで、引数のコードを実行する際のスコープが必ずグローバルになることを利用しています。
実行してみるとわかりますが、次のようなコードにてしっかりと`window`オブジェクトを取得出来ています。

```js
new function() {
    var a = eval('this'); // 直接eval
    var b = (0, eval)('this'); // 間接eval
    
    console.log(a, b); // Object {}, Window {...}
}
```

ただしこの仕様、ES5からのようで、ES3環境では下記のように`Function()`を利用する方法のほうがベターなようです。

```js
new function() {
    var a = Function('return this')();
    
    console.log(a); // Window {...}
}
```

何れにせよ、ここまで来るとなかなか利用する機会はなさそうですね。
安易に利用するとプロジェクトメンバーから黒魔術師認定されてしまうかもしれません。


# 黒魔術（闇レベル：★★★++）


## `++[[]][+[]]+[+[]]`

**文字化けではありません**、まずは実行してみましょう。

![](https://i.gyazo.com/433a749106e6a592b53e474b82c247c6.png)

**1 0 。**

この黒魔術、ご存じの方もいるかもしれません。
元ネタはstackoverflowのJavaScriptカテゴリの[質問](http://stackoverflow.com/questions/7202157/why-does-return-the-string-10)です。
一見ネタにしか見えませんが、実はこの演算、細かく読み解いていくとJavaScriptの理解すべき型変換挙動のエッセンスが詰まっているのではないかと考えています。

この式ですが、まず最初に次のように2つのブロックに分解できます。

```js
++[[]][+[]]
+
[+[]]
```

次に分解された内の後半部分、`[+[]]`は`[0]`となります。
配列は演算対象になった場合、配列自身が持つ`.toString()`が実行されその後演算処理に渡されるという処理フローを辿ります。
よって`+[]`はまず`[].toString()`により`""`に変換され、最終的に`+""`となり、結果`0`となります。

上記演算によって下記の状態になります。

```js
++[[]][0]
+
[0]
```

次に`++[[]][0]`ですが、ここがなかなかに難解です。
`[[]][0]`は`[[]]`という配列の`0`番目を参照している式です。よって`[[]]`の`0`番目、すなわち先頭要素の`[]`が返却されます。
よって`++[[]][0]`は`++[]`という演算結果となります。
配列をインクリメント(`++`)すると、配列が数値化（`0`）された後に`+1`されるのでnumber型の`1`が返却されます。
よって`++[[]][0]`は`1`となり、下記の状態になります。

```js
1
+
[0]
```

しかし、上記演算の中の`++[]`のみを実行すると`Uncaught ReferenceError: Invalid left-hand side expression in prefix operation`というエラーが発生します。

![](https://i.gyazo.com/120caaa2e88d55348fd556f7304ab535.png)

なぜ、`++[[]][0]`はOKで、`++[]`はNGなのでしょうか？
もしかしてバグ？と感じるかもしれませんが、これはJavaScriptの`prefix operation`(++, --, 等)の仕様です。

`prefix operation`は内部的にとあるAPIの呼び出しを行いますが、そのAPIは`参照`を必要とします。
`[]`という式は`参照`を生成しないため`++[]`はエラーとなります。
JavaScriptが`参照`を生成する条件は、式(Expression)が次の状態の場合です。

- 変数を参照しているか
- オブジェクトのプロパティアクセスであるか（例: `[[]][0]`）

その証拠に下記のコードは正しく動作します。
`a`は変数の参照なので、`参照`が生成されます。

```js
var a = [];
++a; // 1
```

一見動きそうな下記の式ですが、`1`という数字は`参照`を生成しないため同様のエラーが発生します。

```js
++1; // Uncaught ReferenceError: Invalid left-hand side expression in prefix operation
```

多少脱線しましたが、これまでの演算結果により下記の状態になりました。

```js
1
+
[0]
```

`[0]`は配列なので演算前に`.toString()`が呼ばれ`"0"`となります。
よって最終的に下記の状態になります。

```js
1
+
"0"
```

JavaScriptでは文字列と数値の演算結果は文字列となるので
`1 + "0"`すなわち`"10"`となるわけです。

**Q.E.D. 証明終了。** と言いたくなるボリュームですね。


## `_=$=+[],++_+''+$`

これも演算すると`"10"`になります。
上記スレッドのコメント内で紹介されているネタですが、こちらも見た目にインパクトがあるのでピックアップしてみました。

こちらは特に難しい事はありません。

２つの式がカンマで連結されており、順番に実行されていきます。

```js
_=$=+[]
,
++_+''+$
```

`_=$=+[]`は`_`, `$`, という変数に対して`+[]`が代入されます。
`+[]`は`0`なので現時点で`_`と`$`は`0`となります。

次に`++_+''+$`ですが、`++_`は`_`をインクリメントするので`1`になり最終的に変数を展開すると下記のような状態になります。


```js
1 + '' + 0
```

`1 + ''`は文字列の`"1"`となり、`"1" + 0`で最終的に`"10"`となります。

`"10"`という文字列を得るためにここまで情熱をかけられるのは凄いですね。是非お友達になりたいです。


# あとがき

JavaScriptにはここで紹介している以外にもたくさんの黒魔術が存在しているかと思います。
みなさんはこれまでどのような魔術に遭遇してきましたでしょうか？

一見パルプンテに見えるコード達も、術式を丁寧に読み解いてみると案外面白いのかもしれません。