2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-06-07

ECMAScript

概要

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

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

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

文法(Syntax)

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

構成要素 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 (変数文)**として定義されており、「文」に分類されます。

上記リンク先の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に至っても、変数系構文の分類が分かりにくい問題は継続しています。

構文名 分類 Syntax(ES2019)
変数文 VariableStatement
let宣言 宣言 LexicalDeclaration
const宣言 宣言 LexicalDeclaration
2
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?