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
- 13 ECMAScript Language: Statements and Declarations - ECMAScript® 2019 Language Specification
構成要素 | Syntaxの名前 | 配下のSyntax例 |
---|---|---|
文 | Statement | ExpressionStatement(式文), VariableStatement(変数文), BlockStatement(ブロック) |
宣言 | Declaration | FunctionDeclaration(関数宣言) |
式 | Expression | AssignmentExpression(割当式), Expression(式) |
あなたがソースコードを書く時、「文」と「宣言」はそのまま書いて実行する事が出来ます。
しかし、「式」だけを書いたソースコードの実行を試みると、SyntaxError
(文法エラー)が発生します。
ソースコードは複数の「文」または「宣言」で構成されなければ、なりません。
「式」は「文」または「宣言」の中で決められた種類の「式」だけを記述可能なようにSyntaxが定められています。
詳細は ECMAScript 2019 の Syntax を追いかけて下さい。
関数宣言(FunctionDeclaration)
「関数宣言」は「宣言」に分類される為、関数宣言のみを書いても、SyntaxError
(文法エラー)は発生しません。
function foo () {} // SyntaxError は発生しない
関数宣言は文法上、次の制約があります。
- function キーワードで始まらなければならない(MUST)
- 名前をつけなければならない(MUST)
前者は見た目通りなので分かると思いますが、後者は次のように「名前を省略して書くことが出来ない」という制約になります。
function () {} // SyntaxError: Function statements require a function name
上述のコメントは私が実行したWebブラウザ「Google Chrome 83.0.4103.61」のエラーメッセージです。
翻訳すると「文法エラー: 関数文には名前が必須です」となりますが、前節の通り、「関数宣言」は「宣言」の分類で「文」ではありませんので、「Function statements」を「Function declarations」に置き換えて解読する必要があります。
式文(Expression)
始めに私は、「式」は「文」または「宣言」の中で決められた種類の「式」だけを記述可能、と説明しました。
しかし、実は「式を単体で書いて動作する文」として「式文」が定義されています。
「式文」は「式」の後ろに ;
(セミコロン)を置くだけのシンプルな構文です。
例えば、「後置インクリメント演算子(Postfix Increment Operator)」を「式文」で書くケースを良く見かけます。
i++;
i++
が「式」、i++;
が「式文」です。
このコードは「文」として成立しているので、SyntaxError
(文法エラー)は発生しません。
関数式(FunctionExpression)
「関数式」を「式文」で書いて見ましょう。
関数式は関数宣言と違い、名前を付けても付けなくても構わない、自由があります。
今回は、名前を付けた関数式(いわゆる「名前付き関数式」)の後ろに ;
(セミコロン)を置いた「式文」を書いてみましょう。
function foo () {}; // SyntaxErrorは発生しない
エラーは発生しませんでしたが、ちょっと待ってください。
**";" が存在しなければ、このコードは「関数宣言」**となります。
もしも、「関数宣言」として実行されていたら、これは「式文」ではないことになります。
「関数式」は「関数宣言」と違い、関数名の名前を持つ変数を定義することはありません。
コンソールで変数 foo
を出力できるのであれば、「関数宣言」として機能している事が証明されます。
console.log(foo); // foo () {}
function foo () {};
変数 foo
が定義されているので、これは「関数宣言」のようです。
なぜ ;
を後ろに書いたのに「式文」にならなかったのでしょうか。
実は、;
には**空文(EmptyStatement)**という「何も実行しない文」が定義されています。
つまり、私が実行したWebブラウザ「Google Chrome 83.0.4103.61」は先のコードを次のように解釈していました。
console.log(foo); // foo () {}
function foo () {} // 関数宣言
; // 空文
このように「式文」とも「関数宣言」とも受け取れるコードを解析する場合の曖昧性を排除する為、ECMAScript2019では「式文」に特別な例外ルールを設けています。
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キーワードで始まらない式に変更したら、どうでしょうか。
(function foo () {}); // SyntaxError は発生しない
foo; // ReferenceError: foo is not defined
()
はグループ化演算子(Grouping Operator)で、演算子の優先順位を変更する為に使われます。
内部に一つだけ関数式を入れても優先順位は変わりませんが、functionキーワードで始まらないコードに変える事が出来ます。
ReferenceError: foo is not defined
で変数 foo
は存在しない事が証明された為、このコードは「関数宣言」ではありません。
期待通り、「式文」として動作させる事が出来ました。
ちなみに、このコードを少し変えてやると、いわゆる即時関数のコーディングパターンとなります。
(※ECMAScript 2019に「即時関数」の用語はありません。俗称です。)
/**
* 名前付き関数式バージョン
**/
(function foo () {}()); // 即時実行する
(function foo () {})(); // 即時実行する
/**
* 無名関数式バージョン
**/
(function () {}()); // 即時実行する
(function () {})(); // 即時実行する
よく知られるのはこの四種類ですが、「functionキーワードで始まらない」のルールさえ守れば良いので、他の演算子をfunctionキーワードの手前に置くことでも即時関数形式に出来ます。
/**
* 名前付き関数式バージョン
**/
+function foo () {}(); // 即時実行する
-function foo () {}(); // 即時実行する
!function foo () {}(); // 即時実行する
/**
* 無名関数式バージョン
**/
+function () {}(); // 即時実行する
-function () {}(); // 即時実行する
!function () {}(); // 即時実行する
これらは ()
と違い、「返り値」に変更を加えています。
従って、即時実行後の返り値に特別な演算をさせる用途において、活用できます。
歴史
MDNの歴史
「関数文」や「関数宣言文」のような「誤った表現」は古くからあり、MDNはその筆頭でした。
今では「関数宣言」と書かれていますが、2016/04/22時点では「function 文」と書かれていました。
前述の通り、「関数文」は仕様に存在しない独自用語なので、MDNのタイトルを「関数宣言」に修正したものと思われます。
ただし、URLは /Statement/ 配下のままで
上記ページタイトルを「文と宣言」に修正して辻褄を合わせたようです。
サブ見出しが「文と宣言(カテゴリ別)」とあるように、カテゴリ別にリストされていますが、「文」と「宣言」を区別できる分類にはなっていません。
例えば、「function」が「宣言」ではなく、「関数とクラス」に分類され、「var」が「宣言」に分類されるという矛盾が発生しています。
おそらく、タイトルが「文」の当時の分類のまま修正されずに残っているのでしょう。
ECMAScriptの歴史
var
はES3からES2019まで **VariableStatement (変数文)**として定義されており、「文」に分類されます。
- 12.2 変数文 (Variable statement) - Under Translation of ECMA-262 3rd Edition
- 13.3.2 Variable Statement - ECMAScript® 2019 Language Specification
上記リンク先のES2019より一文を引用します。
A var statement declares variables that are scoped to the running execution context's VariableEnvironment.
Google機械翻訳結果「varステートメントは、実行中の実行コンテキストのVariableEnvironmentをスコープとする変数を宣言します。」
ようするに、「var文 = 変数宣言を行う文」であり、このあたりの説明が「変数文」ではなく「変数宣言」という構文であるかのように誤解される理由と思われます。
「変数宣言」という用語は「変数を宣言する」の動名詞となり、構文としての名前は「変数文」と記載するのが正解になります。
一方、関数宣言はES3当時から**FunctionDeclaration(関数宣言)**であり、「文」ではありませんでした。
ただし、ES3時点では「宣言(Declaration)」のSyntaxは存在せず、関数宣言の文法上の最上位は FunctionDeclaration でした。
ES6で初めて13章のタイトルが「Statements and Declarations」に変化し、Syntaxに **Declaration (宣言)**が定義されました。
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
- 13.3.2 Variable Statement - ECMAScript® 2019 Language Specification
構文名 | 分類 | Syntax(ES2019) |
---|---|---|
変数文 | 文 | VariableStatement |
let宣言 | 宣言 | LexicalDeclaration |
const宣言 | 宣言 | LexicalDeclaration |