JavaScriptで密かに誤解されていること5選

皆さんこんにちは。この記事では、JavaScriptに関して誤解している人が多そうな小ネタを5個ほど集めましたので紹介します。では早速どうぞ。

## `...`は「スプレッド演算子」ではない

スプレッド演算子ではなく**スプレッド構文**です。

`...`はES2015で追加された便利な構文です。次のように配列をコピーしたり、関数呼び出しの引数に配列を展開したりできます。

```js:スプレッド構文の例
const arr1 = [1, 2, 3];

const arr2 = [...arr1, 4, 5];

func(...arr1, ...arr2);
```

また、ES2018からはオブジェクトの中でも`...`が使えます。

当初この`...`を「スプレッド演算子」（spread operator）と呼ぶ向きがありましたが、よく見るとこれは全然演算子ではありませんね。

演算子の定義は人によって異なるかもしれませんが、「いくつかの式から式を作る働きをする構文」というのが一般に受け入れられている定義だと思います。例えば`x + 1`という式は、`x`という式と`1`という式を`+`で繋げる事で`x + 1`という式を得ています。この働きをする`+`が演算子です。

こうしてみると、`...`は式を作るのではありません。つまり、`const arr2 = ...arr1;`のようなものは受け付けられないということです。`...`が使えるのは配列リテラル・オブジェクトリテラルの中と関数引数の中だけであり、どちらかといえばこれらの構文に組み込まれたものであると見るべきです。

ですから、`...`は演算子とは呼びがたいのです。実際、[ECMAScript仕様書](https://www.ecma-international.org/ecma-262/6.0/)を見ても`...`を「演算子」と呼んでいるところはありません。

実は明確に「スプレッド構文」と呼んでいるわけでもなく単にspreadと呼ばれているだけなのですが、[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)がspread syntaxという名前を採用していることなどからここではスプレッド構文という名前を用いて説明しています。

なお、さらっと**ECMAScript仕様書**という概念が出てきましたが、これはJavaScriptというプログラミング言語の仕様を決めるもので、Ecma Internationalという国際機関によって管理されています。仕様書は現在では年に1回くらい更新され、JavaScriptの機能追加が行われます。ES何とかというのはJavaScriptのバージョン名というわけです。

ECMAScript仕様書はJavaScriptに関する絶対に正しい事実源ですので、皆さんも困ったらぜひ参照してみてください。

では次のネタに移ります。

## `async/await`はES7ではない

**ES8です。**

`async/await`は`Promise`を用いた非同期処理をいい感じに扱うためのすてきな構文ですが、これが導入されたECMAScriptバージョンが「ES7」であるとの勘違いがよく見られます。「ES7 async/await」のような言い方をしているのをたまに見ますね。

当初の予定はそうだったのかもしれませんが、実際のところ`async/await`が導入されたのはES8です。

## そもそもES7とかES8は正式名称ではない

これらは古い呼び方であり、**ES2016**や**ES2017**という言い方が正しいです。

ES5が登場したのが2009年で、その次のバージョンはHarmonyとかES6とか呼びながら長い間議論されてきました。それゆえ、ES6という名前に聞き覚えがある人は多いでしょう。

しかし、最終的にはES5の次のバージョンは**ES2015**が正式名称になりました。すなわち、ES6と**ES2015**は同じものを指しており、ES6というのは古い呼び方ということになります。一の位がひとつずれていてややこしいので気をつけましょう。

この名残で、その次のバージョンでありES2016をES7と呼び、ES2017をES8と呼び……という呼び方がたまに見られます。現行の最新バージョンであるES2019はES10になるのでしょうか。

ちなみに、6, 7, 8……というナンバリングが完全に消滅したわけではありません。[ES2019の仕様書](https://www.ecma-international.org/ecma-262/10.0/)のタイトルを見るとこのように書かれています。

<img width="523" alt="スクリーンショット 2019-06-27 14.12.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233667/79fa12d1-16ce-59ae-ea89-3669e14bd465.png">

ECMA-262, **10th edition**, June 2019
ECMAScript®︎ 2019
Language Specification

ということで、最初に10th editionと書いてありますね。これは、**言語の名称ではなくなったが、仕様のバージョン番号としては生きている**ということになります。

ややこしいですが、勘違いしないでいただきたいのは**じゃあES10のような言い方が正式に正しいかというと、そうではない**ということです。仕様書の最初の2文を引用します。

> This Ecma Standard defines the ECMAScript 2019 Language. It is the tenth edition of the ECMAScript Language Specification.

ここに書いてあることが答えですね。言語の名前（あるいは言語のバージョン番号）がES2019で、仕様書のバージョンが10なのです。昔（ES5まで）は両者は一緒でしたが、今は違うというわけです。

次はちょっと話が変わります。

## オブジェクトのキーの列挙順は全く保証されていないわけではない

**ES2015以降のメソッドを使う場合は列挙順が保証されています。**（`Object.defineProperties`も、ES5ですが例外的に列挙順が保証されています。）

また、**`for-in`などの古い方法の場合もできるだけ仕様化しようぜという議論があります**（[現在Stage 2](https://github.com/tc39/proposal-for-in-exploration)）。

実は、`for-in`とか`Object.keys`を使ってオブジェクトのキーを列挙する場合の順番は実装依存で、仕様で並び順が定義されているわけではありませんでした。なので「JavaScriptではオブジェクトのキーの列挙順は保証されていない」と思っている人がいるようです。

しかし100%保証されていないわけではなく、最近のAPI（`Object.getOwnPropertyNames`など）はちゃんと列挙順が保証されています。その一方、ES5以前のメソッドでは後方互換性のために列挙順が定義されないままになっています（保証されていなかったものを保証すると後方互換性が崩れるというのも変な話ですが）。

筆者が書いた記事ではありませんが詳しい記事がありましたのでこちらに説明を譲ります。

- [[JavaScript] オブジェクト/Mapのキーの列挙順は保証されるのか](https://qiita.com/anqooqie/items/ab3fed5d269d278cdd2b)

## オブジェクトを真偽値に変換すると必ず`true`になるわけではない

**`document.all`を真偽値に変換すると`false`になります。**

JavaScriptでは原則としてオブジェクトを真偽値に変換すると`true`になりますが、この`document.all`だけは例外です。`document.all`はオブジェクトなので`document.all.foo`のようにプロパティアクセスができたりするのですが、それにも関わらず`document.all`は真偽値に変換すると`false`なのです。また、`document.all`が持つ特異な挙動はそれだけではなく、`document.all == undefined`が`true`になったり`typeof document.all`が`"undefined"`になるなどの特徴もあります。

もっとも、`document.all`などというものは**古代の遺物**であり**Web界の黒歴史**なので、皆さんがこの事実を気にかける必要はまったくありません。なぜこんな挙動があるのかというのは皆さんのご想像（[あと別の記事](https://qiita.com/jkr_2255/items/f9b7218d7a2b54424c12)）にお任せしますが、JavaScript（というかWeb）は後方互換性を大事にする業界なので今でもこの黒歴史を背負って生きているのです。

なお、この「オブジェクトだけど真偽値に変換すると`false`になる」という挙動は[[[IsHTMLDDA]]内部スロット](https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot)としてECMAScript仕様書に記載されていますが、そのような挙動を実際に体現する`document.all`というオブジェクトの存在はHTML仕様書で定義されています。

昔はECMAScript仕様書にそのような記載はなく、HTML仕様書が「このオブジェクトは真偽値に変換すると`false`になるんでよろしく＾＾」と勝手に書いているという状態でした。この状況に変化があったのが2016～2017年のことで、最終的にこの挙動の定義はECMAScriptに移管されました。そのときの議論は[ecma262リポジトリのissue](https://github.com/tc39/ecma262/issues/668)で見ることができます。

発端は「言語の基礎的な仕様がHTML仕様書に勝手に変更されていいのか？　我々で管理すべきでは？」という意見です。この議論では「HTMLの仕様書に書いておけばそれでいい派」「ちゃんとECMAScript仕様書に載せるべき派」「ECMAの管轄にしたいけどECMAScript仕様書には入れたくない派」などが戦いました。なかなか面白いので興味がある方は読んでみていただきたいのですが、「JavaScriptはWebブラウザだけが実装するものではない。こんなWebの黒歴史のためにブラウザ以外の環境にまで影響を与える（＝ECMAScript仕様書に取り入れる）のはどうなのか」とか「この挙動がECMAScript仕様書に書いていないとECMAScript準拠のブラウザが存在できなくなってしまう」などの意見が出ました。一時は`document.all`を説明するためのULEO (undefined-like exotic object) なる概念を導入する方向に進みましたが、これを仕様書に導入するとそのうち誰かが`document.all`以外のULEOという新たな黒歴史を生み出すかもしれないという懸念がありました。しかし、ECMAScriptの仕様書に「HTMLの`document.all`のみがULEOである」と書くのも何だか微妙ですね。

という感じであまり議論がまとまらない中、最終的に議論は2016年9月のTC39ミーティング（ECMAScriptの仕様に関する意思決定が行われるミーティングです）に持ち込まれました（[議事録](https://tc39.es/tc39-notes/2016-09_sept-27.html#11id-uleo-undefined-like-exotic-object)）。ここでもだいたい同じような議論が行われましたが、ULEOという名前は`document.all`以外にも使われそうなのでやっぱりやめようということになり、ここでHTMLDDAという名前が誕生しました。これはHTML Document Dot Allのことで、明らかに`document.all`専用という雰囲気が出ていますね。最終的には、前述のように[[IsHTMLDDA]]内部スロットがあるオブジェクトのみがこの挙動をすることになりました。要するに、ECMAScript仕様書の中にHTMLという概念が登場してしまうという部分は妥協しつつ、この変な挙動がさらに拡散をするのを防ぐ選択を取ったことになります。

ECMAScript仕様書にこの変更が反映されると、HTML仕様書のほうも書き直され、「`document.all`は（正確には`HTMLAllCollection`オブジェクトは）[[IsHTMLDDA]]を持つ」という定義に変更されました。めでたしめでたしですね。

## まとめ

皆さんはいくつ誤解していましたか？　全部知ってたという方はおめでとうございます。ぜひTwitterとかはてなブックマークで自慢してください。（この記事へのリンクとあと**いいね**を忘れずに！）