ECMAScript 2019の文法をコードで理解する

# ECMAScript

## 概要

JavaScriptは**ECMAScript**というWeb標準仕様を実装されています。
本文書では主に、ECMAScriptを元にした解説を行います。

※「ECMAScript」を略して「ES」と表記する事があり、次のように省略されます。

|正式名称|略称|
|:--|:--|
|ECMAScript 3|ES3|
|ECMAScript 6|ES6|
|ECMAScript 2019|ES2019|

## 文法(Syntax)

ECMAScript2019では、ソースコード上の「最も構成となる要素」を3つに大別しています。
(※仕様を完全理解しているとは断言できない為、他にも要素がある可能性はあります。
詳細はECMAScript2019の仕様書を参照して下さい。)

- [12 ECMAScript Language: Expressions - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-ecmascript-language-expressions)
- [13 ECMAScript Language: Statements and Declarations - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-ecmascript-language-statements-and-declarations)

|構成要素|Syntaxの名前|配下のSyntax例|
|:--|:--|:--|
|**文**|Statement|ExpressionStatement(式文), VariableStatement(変数文), BlockStatement(ブロック)|
|**宣言**|Declaration|FunctionDeclaration(関数宣言)|
|**式**|Expression|AssignmentExpression(割当式), Expression(式)|

あなたがソースコードを書く時、「文」と「宣言」はそのまま書いて実行する事が出来ます。

しかし、「式」だけを書いたソースコードの実行を試みると、`SyntaxError` (文法エラー)が発生します。
ソースコードは複数の「文」または「宣言」で構成されなければ、なりません。

「式」は「文」または「宣言」の中で決められた種類の「式」だけを記述可能なようにSyntaxが定められています。
詳細は ECMAScript 2019 の Syntax を追いかけて下さい。

## 関数宣言(FunctionDeclaration)

- [14.1 Function Definitions - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-function-definitions)

「関数宣言」は「宣言」に分類される為、関数宣言のみを書いても、`SyntaxError` (文法エラー)は発生しません。
```JavaScript
function foo () {} // SyntaxError は発生しない
```

関数宣言は文法上、次の制約があります。

- **function キーワード**で始まらなければならない(MUST)
- 名前をつけなければならない(MUST)

前者は見た目通りなので分かると思いますが、後者は次のように「名前を省略して書くことが出来ない」という制約になります。

```JavaScript
function () {} // SyntaxError: Function statements require a function name
```

上述のコメントは私が実行したWebブラウザ「Google Chrome 83.0.4103.61」のエラーメッセージです。
翻訳すると「文法エラー: 関数文には名前が必須です」となりますが、前節の通り、「関数宣言」は「宣言」の分類で「文」ではありませんので、「Function statements」を「Function declarations」に置き換えて解読する必要があります。

## 式文(Expression)

始めに私は、「式」は「文」または「宣言」の中で決められた種類の「式」だけを記述可能、と説明しました。
しかし、実は「式を単体で書いて動作する文」として「式文」が定義されています。

- [13.5 Expression Statement - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-expression-statement)

「式文」は「式」の後ろに `;` (セミコロン)を置くだけのシンプルな構文です。
例えば、「後置インクリメント演算子(Postfix Increment Operator)」を「式文」で書くケースを良く見かけます。

- [12.4.4 Postfix Increment Operator - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-postfix-increment-operator)

```JavaScript
i++;
```

`i++` が「式」、`i++;` が「式文」です。
このコードは「文」として成立しているので、`SyntaxError` (文法エラー)は発生しません。

## 関数式(FunctionExpression)

「関数式」を「式文」で書いて見ましょう。

- [14.1 Function Definitions - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-function-definitions)

関数式は関数宣言と違い、名前を付けても付けなくても構わない、自由があります。
今回は、名前を付けた関数式(いわゆる「名前付き関数式」)の後ろに `;`(セミコロン)を置いた「式文」を書いてみましょう。

```JavaScript
function foo () {}; // SyntaxErrorは発生しない
```

エラーは発生しませんでしたが、ちょっと待ってください。

**";" が存在しなければ、このコードは「関数宣言」**となります。
もしも、「関数宣言」として実行されていたら、これは「式文」ではないことになります。

「関数式」は「関数宣言」と違い、関数名の名前を持つ変数を定義することはありません。
コンソールで変数 `foo` を出力できるのであれば、「関数宣言」として機能している事が証明されます。

```JavaScript
console.log(foo); // foo () {}
function foo () {};
```

変数 `foo` が定義されているので、これは「関数宣言」のようです。
なぜ `;` を後ろに書いたのに「式文」にならなかったのでしょうか。

実は、`;` には**空文(EmptyStatement)**という「何も実行しない文」が定義されています。

- [13.4 Empty Statement - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-empty-statement)

つまり、私が実行したWebブラウザ「Google Chrome 83.0.4103.61」は先のコードを次のように解釈していました。

```JavaScript
console.log(foo);   // foo () {}
function foo () {}  // 関数宣言
;                   // 空文
```

このように「式文」とも「関数宣言」とも受け取れるコードを解析する場合の曖昧性を排除する為、ECMAScript2019では「式文」に特別な例外ルールを設けています。

- [13.5 Expression Statement - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-expression-statement)

> NOTE: An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block.
> An ExpressionStatement cannot start with the function or class keywords because that would make it ambiguous with a FunctionDeclaration, a GeneratorDeclaration, or a ClassDeclaration.
> An ExpressionStatement cannot start with async function because that would make it ambiguous with an AsyncFunctionDeclaration or a AsyncGeneratorDeclaration.
> An ExpressionStatement cannot start with the two token sequence let [ because that would make it ambiguous with a let LexicalDeclaration whose first LexicalBinding was an ArrayBindingPattern.

以下、Google機械翻訳結果。

- 注: ExpressionStatementをU + 007B（LEFT CURLY BRACKET）で開始することはできません。
  これは、Blockがあいまいになる可能性があるためです。 
- **ExpressionStatementをfunctionまたはclassキーワードで始めることはできません。**
  これは、FunctionDeclaration、GeneratorDeclaration、またはClassDeclarationがあいまいになるためです。 
- AsyncFunctionDeclarationまたはAsyncGeneratorDeclarationがあいまいになるため、ExpressionStatementを非同期関数で始めることはできません。 ExpressionStatementは、2つのトークンシーケンスlet [で始めることはできません。
  これは、最初のLexicalBindingがArrayBindingPatternであるlet LexicalDeclarationで曖昧になるためです。」

「式文はfunctionキーワードで始められない」ので、先のコードが関数宣言として解釈される事は予め決まっていました。
では、**functionキーワードで始まらない式**に変更したら、どうでしょうか。

- [12.2.10 The Grouping Operator - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-grouping-operator)

```JavaScript
(function foo () {}); // SyntaxError は発生しない
foo;                  // ReferenceError: foo is not defined
```

`()` はグループ化演算子(Grouping Operator)で、演算子の優先順位を変更する為に使われます。
内部に一つだけ関数式を入れても優先順位は変わりませんが、**functionキーワードで始まらないコードに変える**事が出来ます。
`ReferenceError: foo is not defined` で変数 `foo` は存在しない事が証明された為、このコードは「関数宣言」ではありません。
期待通り、「式文」として動作させる事が出来ました。

ちなみに、このコードを少し変えてやると、いわゆる**即時関数**のコーディングパターンとなります。
(※ECMAScript 2019に「即時関数」の用語はありません。俗称です。)

```JavaScript
/**
 * 名前付き関数式バージョン 
 **/
(function foo () {}()); // 即時実行する
(function foo () {})(); // 即時実行する

/**
 * 無名関数式バージョン 
 **/
(function () {}()); // 即時実行する
(function () {})(); // 即時実行する
```

よく知られるのはこの四種類ですが、「functionキーワードで始まらない」のルールさえ守れば良いので、他の演算子をfunctionキーワードの手前に置くことでも即時関数形式に出来ます。

```JavaScript
/**
 * 名前付き関数式バージョン 
 **/
+function foo () {}(); // 即時実行する
-function foo () {}(); // 即時実行する
!function foo () {}(); // 即時実行する

/**
 * 無名関数式バージョン 
 **/
+function () {}(); // 即時実行する
-function () {}(); // 即時実行する
!function () {}(); // 即時実行する
```

これらは `()` と違い、「返り値」に変更を加えています。
従って、即時実行後の返り値に特別な演算をさせる用途において、活用できます。

# 歴史

## MDNの歴史

「関数文」や「関数宣言文」のような「誤った表現」は古くからあり、MDNはその筆頭でした。

- [関数宣言 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function)

今では「関数宣言」と書かれていますが、2016/04/22時点では「function 文」と書かれていました。

- [JavaScript - 関数と変数の巻き上げの違いとは｜teratail](https://teratail.com/questions/33002#reply-52159)

前述の通り、「関数文」は仕様に存在しない独自用語なので、MDNのタイトルを「関数宣言」に修正したものと思われます。
ただし、URLは /Statement/ 配下のままで

- [文と宣言 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements)

上記ページタイトルを「文と宣言」に修正して辻褄を合わせたようです。
サブ見出しが「文と宣言（カテゴリ別）」とあるように、カテゴリ別にリストされていますが、「文」と「宣言」を区別できる分類にはなっていません。
例えば、「function」が「宣言」ではなく、「関数とクラス」に分類され、「var」が「宣言」に分類されるという矛盾が発生しています。
おそらく、タイトルが「文」の当時の分類のまま修正されずに残っているのでしょう。

## ECMAScriptの歴史

`var` はES3からES2019まで **VariableStatement (変数文)**として定義されており、「文」に分類されます。

- [12.2 変数文 (Variable statement) - Under Translation of ECMA-262 3rd Edition](http://www2u.biglobe.ne.jp/~oz-07ams/2002/ecma262r3/12_Statements.html#section-12.2)
- [13.3.2 Variable Statement - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-variable-statement)

上記リンク先のES2019より一文を引用します。

> A var statement declares variables that are scoped to the running execution context's VariableEnvironment.

Google機械翻訳結果「varステートメントは、実行中の実行コンテキストのVariableEnvironmentをスコープとする変数を宣言します。」

ようするに、「var文 = 変数宣言を行う文」であり、このあたりの説明が「変数文」ではなく「変数宣言」という構文であるかのように誤解される理由と思われます。
「変数宣言」という用語は「変数を宣言する」の動名詞となり、構文としての名前は「変数文」と記載するのが正解になります。

---

一方、関数宣言はES3当時から**FunctionDeclaration(関数宣言)**であり、「文」ではありませんでした。

- [13 関数定義 (Function Definition) - Under Translation of ECMA-262 3rd Edition](http://www2u.biglobe.ne.jp/~oz-07ams/2002/ecma262r3/13_Function_Definition.html)

ただし、ES3時点では「宣言(Declaration)」のSyntaxは存在せず、関数宣言の文法上の最上位は FunctionDeclaration でした。

ES6で初めて13章のタイトルが「Statements and Declarations」に変化し、Syntaxに **Declaration (宣言)**が定義されました。

- [13 ECMAScript Language: Statements and Declarations - ECMAScript 2015 Language Specification – ECMA-262 6th Edition](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-statements-and-declarations)

> ```
> Declaration :
>   HoistableDeclaration
>   ClassDeclaration
> 
> LexicalDeclaration :
>   HoistableDeclaration
>   FunctionDeclaration
>   GeneratorDeclaration
> 
> HoistableDeclaration :
>   FunctionDeclaration
>   GeneratorDeclaration
> ```

class宣言(ClassDeclaration)、レキシカル宣言(LexicalDeclaration)が生まれた時に、関数宣言(FunctionDeclaration)のSyntaxも再定義されたようです。

しかし、変数文(VariableStatement)は再定義されなかったので、ES2019に至っても、変数系構文の分類が分かりにくい問題は継続しています。

- [13.3.1 Let and Const Declarations - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-let-and-const-declarations)
- [13.3.2 Variable Statement - ECMAScript® 2019 Language Specification](http://www.ecma-international.org/ecma-262/10.0/#sec-variable-statement)

|構文名|分類|Syntax(ES2019)|
|:--|:--|:--|
|変数文|文|VariableStatement|
|let宣言|宣言|LexicalDeclaration|
|const宣言|宣言|LexicalDeclaration|