文と式

本格的に基本文法について学ぶ前に、JavaScriptというプログラミング言語がどのような要素からできているかを見ていきましょう。

JavaScriptは、(Statement)と(Expression)から構成されています。

(Expression)を簡潔に述べると、値を生成し、変数に代入できるものを言います。

42のようなリテラルやfooといった変数、関数呼び出しが式です。 また、1 + 1のような式と演算子の組み合わせも式と呼びます。

式の特徴として、式を評価すると結果の値を得ることができます。 この結果の値を評価値と呼びます。

評価した結果を変数に代入できるものはであるという理解で問題ありません。

// 1という式の評価値を表示
console.log(1); // => 1
// 1 + 1という式の評価値を表示
console.log(1 + 1); // => 2
// 式の評価値を変数に代入
const total = 1 + 1;
// 関数式の評価値(関数オブジェクト)を変数に代入
const fn = function() {
    return 1;
};
// fn() という式の評価値を表示
console.log(fn()); // => 1

(Statement)を簡潔に述べると、処理する1ステップが1つの文と言えます。 JavaScriptでは、文の末尾にセミコロン(;)を置くことで文と文に区切りをつけます。

ソースコードとして書かれた文を上から処理していくことで、プログラムが実行されます。

処理する文;
処理する文;
処理する文;

たとえば、if文やfor文などがと呼ばれるものです。 次のように、文の処理の一部として式を含むことがあります。

const isTrue = true;
// isTrueという式がif文の中に出てくる
if (isTrue) {
}

一方、if文などは文であり式にはなれません。

ではないため、if文を変数へ代入することはできません。 次のようなコードは構文として問題があるため、構文エラー(SyntaxError)となります。

// 構文として間違っているため、SyntaxErrorが発生する
const forIsNotExpression = if (true) { /* ifは文であるため式にはなれない */ }

式文

一方で、(Expression)は(Statement)になれます。文となった式のことを式文と呼びます。 基本的に文が書ける場所には式を書けます。

その際に、式文(Expression statement)は文の一種であるため、セミコロンで文を区切っています。

// 式文であるためセミコロンをつけている
式;

式は文になれますが、先ほどのif文のように文は式になれません。

ブロック文

次のような、文を{}で囲んだ部分をブロックと言います。 ブロックには、複数のが書けます。

{
    文;
    文;
}

ブロック文は単独でも書けますが、基本的にはif文やfor文など他の構文と組み合わせて書くことがほとんどです。 次のコードでは、if文とブロック文を組み合わせることで、if文の処理内容に複数の文を書いています。

// if文とブロック文の組み合わせ
if (true) {
    console.log("文1");
    console.log("文2");
}

文の末尾にはセミコロンをつけるとしていましたが、 例外としてブロックで終わる文の末尾には、セミコロンが不要となっています。

// ブロックで終わらない文なので、セミコロンが必要
if (true) console.log(true);
// ブロックで終わる文なので、セミコロンが不要
if (true) {
    console.log(true);
}

[コラム] 単独のブロック文の活用

アプリケーションのソースコードにif文などと組み合わせない単独のブロック文を書くことはほとんどありません。 しかし、REPLで同じコードの一部を変更して実行を繰り返している場合には、単独のブロック文が役に立つ機会もあります。

REPLでは、次のように同じ変数名を再定義すると、構文エラーが発生します(詳細は「変数と宣言」の章の「varの問題」を参照)。 そのため、同じコードを再び実行するには、ブラウザでページをリロードして変数定義をリセットしないといけませんでした。

// REPLでの動作。»はREPLの入力欄
» const count = 1;
undefined
» const count = 2;
SyntaxError: redeclaration of const count

この問題は単独のブロック文で変数定義を囲むことで回避できます。 ブロック文({})の中でletやconstを用いて変数を定義しても、そのブロック文の外には影響しません。 そのため、次のようにブロック文で囲んでおけば、同じ変数名を定義しても構文エラー(SyntaxError)にはなりません。

// REPLでの動作。»はREPLの入力欄
» {
    const count = 1;
}
undefined // ここでブロック内で定義した変数`count`は参照できなくなる
» {
    const count = 1;
}
undefined // ここでブロック内で定義した変数`count`は参照できなくなる

これは、ブロックスコープという仕組みによるものですが、詳しい仕組みについては「関数とスコープ」の章で解説します。 今は、ブロック文を使うとREPLでの試行錯誤がしやすいということだけ知っていれば問題ありません。

関数宣言(文)と関数式

関数と宣言」の章において、関数を定義する方法を学びました。 functionキーワードから文を開始する関数宣言と、変数へ関数式を代入する方法があります。

関数宣言(文)と関数式は、どちらもfunctionというキーワードを利用しています。

// learn関数を宣言する関数宣言文
function learn() {
}
// 関数式をread変数へ代入
const read = function() {
};

この文と式の違いを見ると、関数宣言文にはセミコロンがなく、関数式にはセミコロンがあります。 このような違いがなぜ生まれるのかは、ここまでの内容から説明できます。

関数宣言(文)で定義したlearn関数には、セミコロンがありません。 これは、ブロックで終わる文にはセミコロンが不要であるためです。

一方、関数式をread変数へ代入したものには、セミコロンがあります。

「ブロックで終わる関数であるためセミコロンが不要なのでは?」と思うかもしれません。

しかし、この匿名関数はであり、この処理は変数を宣言する文の一部であることがわかります。 つまり、次のように置き換えても同じと言えるため、末尾にセミコロンが必要となります。

function fn() {}
// fn(式)の評価値を代入する変数宣言の文
const read = fn;

まとめ

この章では次のことについて学びました。

  • JavaScriptは(Statement)と(Expression)から構成される
  • 文は式になれない
  • 式は文になれる(式文
  • 文の末尾にはセミコロンをつける
  • ブロックで終わる文は例外的にセミコロンをつけなくてよい

JavaScriptには、特殊なルールに基づき、セミコロンがない文も行末に自動でセミコロンが挿入されるという仕組みがあります。 しかし、この仕組みは構文を正しく解析できない場合に、セミコロンを足すという挙動を持っています。 これにより、意図しない挙動を生むことがあります。そのため、必ずの末尾にはセミコロンを書くようにします。

エディターやIDEの中にはセミコロンの入力の補助をしてくれるものや、ESLintなどのLintツールを使うことで、 セミコロンが必要なのかをチェックできます。

セミコロンが必要か見分けるにはある程度慣れが必要ですが、ツールを使って静的にチェックできます。 そのため、ツールなどの支援を受けて経験的に慣れていくこともよい方法と言えます。