JavaScriptの { } を理解する

いきなりですが、自分の好きなREPLで以下を入力してみてください[^repl_desc]。

[^repl_desc]: REPLとは、JavaScriptを入力するとその場で結果を返してくれるやつです。ブラウザの開発者ツールが一番身近だと思います。あと、node.jsもREPLがついています。

```js
{a: 10}
```

結果はどうなったでしょうか。

自分が今使っているGoogle Chromeだとこうなりました。

[![Screenshot from Gyazo](https://gyazo.com/5131ae92d99137b5be85b76ac21da2f6/raw)](https://gyazo.com/5131ae92d99137b5be85b76ac21da2f6)

結果は`{a: 10}`というオブジェクトです。まあ、これは当然ですね。`3 + 5`と入力すれば実行されて`8`が返ってくるのですから、`{a: 10}`というオブジェクトリテラルを書けば`{a: 10}`というオブジェクトが作られるのは当然です。

……。

ここで、一部の人は「**おいふざけんなよ**」と思っているかもしれません。というのも、この例は環境によっては違う結果になるのです。具体的には、Chrome以外[^chrome_blink]のブラウザのREPL（FirefoxやEdgeなど）が該当します。あと、ts-nodeのREPLも該当するらしいです。これらの環境では、結果は`{a: 10}`ではなく次のようになります。

[^chrome_blink]: Chromium系はChromeと同じ挙動をするのでOperaとかはChromeと同じです。

```js
10
```

オブジェクトを作ったはずなのに結果が`10`とか**意味不明**ですね。そもそも、こんな簡単なプログラムで結果が全然違うとか、**JavaScriptは欠陥言語**もいいところだし巷の**ブラウザはバグだらけ**ですね。<font color="#f6f6f6">（うそです）</font>

そこでこの記事では、なぜこのような挙動になるのかについて解説します。そんな説明聞かなくても分かってるよという方は既にこの記事の内容をマスターしているので大丈夫です。

以降、この記事で仕様書という場合は[ECMAScript® 2018 Language Specification](https://www.ecma-international.org/ecma-262/9.0/index.html)を指すものとします。

# 2種類の結果の説明
まず、上で見た2種類の結果の解釈をそれぞれ説明します。

## `{a: 10}`
`{a: 10}`は言うまでもなく**オブジェクトリテラル**、すなわち新しいオブジェクトを作る式です。式をREPLに入力したら、その式が評価されて結果が表示されるというのは皆さんご存知の通りです。

ですので、`{a: 10}`を評価した結果のオブジェクトである`{a: 10}`が表示されたことになります。これは簡単ですね。

## `10`
では、同じ`{a: 10}`という入力で`10`が表示されたのは何が起こったのでしょうか。実は、この場合は`{ }`がオブジェクトリテラルではなく**ブロック**として解釈されています。

### ブロック

ブロックは**文の1種**であり、`{ 文; ...; 文; }` という構文で複数の文をまとめるものです。ブロックの1つの用途はブロックスコープを導入することです。

```js:ブロックスコープの例
let a = 3;
{
  // このブロックスコープ内でaを宣言
  let a = 100;
  console.log(a); // 100
}
// ブロック内で宣言されたaは外に影響を及ぼさない
console.log(a) // 3
```

また、if文などにも`{ ... }`の形が出てきますが、あれも実はブロックです。というのも、JavaScriptのif文の構文は`if (式) 文` という形であり、`文`のところに文の一種であるブロックを当てはめることでよく見知った`if (式) { 文; ... }`という形が出てくることになります。

### ラベル付き文

`{a: 10}`に話を戻すと、`{ }`がブロックだったので、その中の`a: 10`が文であるということになります。そもそも文の終わりを示すセミコロン`;`が無いじゃんと思うかもしれませんが、JavaScriptにはかの悪名高き自動セミコロン挿入機能がありますので、実はこれは自動的にセミコロンが補われて`{ a: 10; }`になっています。

さて、この`a: 10;`という文は**ラベル付き文** (Labelled Statement)です。ラベル付き文は`ラベル: 文`という形をした文であり[^labeled_funcdecl]、この例では`a`がラベルです。すなわち、`10;`という文に`a:`というラベル部分がついた文なのです。

[^labeled_funcdecl]: 実は関数宣言にもラベルを付けることができますが。

ラベル付き文は、`continue`文や`break`文と組み合わせて使います。`continue`文や`break`文にラベルを指定することで、ネストしたループ内で外側のループを抜けるようなことができます。

```js:ラベル付き文の例
/**
 * xsの要素が全部ysにも含まれるかどうか調べる関数
 * （計算量に関するツッコミは受け付けていません）
 */
function arrSubsetAsSet(xs, ys) {
  outerLoop:
    for (const x of xs) {
      for (const y of ys) {
        if (x === y) {
          continue outerLoop;
        }
      }
      return false;
    }
  return true;
}

console.log(arrSubsetAsSet([1, 3], [5, 3, 2, 1])); // true
```

この例では、for文にラベルを付けることで、`continue`文で外側のfor文を制御しています。ラベルを使うのは`continue`と`break`だけなのでループ以外の文にラベルを付ける意味はありませんが、文法上は可能になっています。

## 式文

話を戻すと、`a: 10;`という文は`10;`という文にラベルが付いたものでした。では`10;`とはなんでしょうか。実はこれは**式文** (Expression Statement)であり、`式;`という形をした文です。式文は、ただその式を評価するだけの文です。式の評価結果は捨てられます。この例だと、`10;`という文は式`10`を評価するだけです。式`10`を評価しても何も起こりませんから、この文は実質何もしないようなものです。

そんなの何の意味があるんだと思うかもしれませんが、実際のところ、皆さんも式文は非常によく使っているはずです。例えば、以下の文は式文です。

```js
console.log(123);
```

この文は、`console.log(123)`という式に`;`がついた文になっています。よくよく考えれば関数呼び出しは式ですから、関数を呼び出す文は式文であるというわけです。他の例としては、`x = 3;`のような代入文も式文です。この2つが代表的な例でしたが、最近は`await`式の式文を書くことが多くなったかもしれません。

以上で`{a: 10}`の2つ目の解釈の説明は終わりです。結局のところ、`10;`という文にラベルを付けてブロックで囲ったものとして解釈されていたというわけです。

## 文の結果
しかし、まだ1つ疑問が残っていますね。`{a: 10}`を実行すると`10`と表示された、あの`10`は何なのかということです。

そもそも、式というのは計算（評価）されて結果が得られるものです。例えば`1 + 2`という式は評価されて`3`という値になります。関数呼び出しの式を評価するときは、その関数が実行されます。

その一方で、**文は結果を返さない**はずです。だいたい、`10;`の結果が`10`という話ならまだ納得できるかもしれませんが、`var x, y;`の結果とか、`continue;`の結果、あるいはfor文の結果は何かみたいな話をされても困るわけで、文の結果という概念を考えること自体がナンセンスに思えます。

**しかしながら**、実はJavaScriptは文の結果などという意味不明な概念を持っています。実際のところ文の結果をプログラムから利用する方法はほぼありませんが[^eval]、**REPLで文を実行したらその結果が表示される**のです。`{a: 10}`を入力して`10`が表示されたのは、`{a: 10}`という文の結果として`10`が表示されたのです。

[^eval]: 後で触れますが、現時点で唯一の方法が[eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)です。

このことは、以下の規則から説明できます。

- 式文`式;`の結果は`式`の評価結果です。
- ラベル付き文`ラベル: 文`の結果は右の`文`の結果です。
- ブロックの評価結果は最後の文の結果です[^block_result]。

[^block_result]: あとで説明しますが、厳密には結果の無い文が混ざったときの挙動が少し異なります。

これにより、`{a: 10}`は文1つだけからなるブロックなので、その結果は`a: 10;`の結果となります。ラベル付き文の結果は元の（ラベルの付いていない）文の結果と同じなので、今回は`10;`の結果となります。式`10`の評価結果は`10`なので、文`10;`の結果は`10`となります。

以上から、`{a: 10}`の文の結果が`10`となったのです。

## 注意点
たった今説明した`{a: 10}`の結果が`10`となるのは、あくまで`{a: 10}`を文として解釈した場合です。以下の文を実行した場合は、`x`に入るのは`10`ではなく`{a: 10}`です。

```js
const x = {a: 10};
```

なぜなら、変数宣言は`変数 = 式`という形であり[^destruction_note]、`=`の右辺に来るのは文ではなく式ではなければならないため、`{a: 10}`が文として解釈されることはないからです（そもそも、文の結果というのは先にも述べたとおり「意味不明な概念」であり、文の結果を変数に代入するなどという意味不明なことは考えるべきではありません）。`{a: 10}`を式として解釈する場合はオブジェクトリテラルとして解釈するしかないため、`x`に入るのは`{a: 10}`というオブジェクトになります。

[^destruction_note]: 分解代入などもあるので実は必ずしもこうではありませんが省略しています。

そもそも、**ブロックは式ではない**という点は、JavaScriptにあまり慣れていない人にとっては注意すべき点かもしれません。実際のところ、ブロックが式であるような言語も存在します（Rustなど）。

```rust:Rustのブロックの例
fn main() {
  //      ↓式の位置にブロックを書くことができる
  let x = {
    // ブロック内で変数宣言も可能
    let foo = 1i64;
    // ブロックの最後に書いた値がブロックの値となる
    foo * 10
  };
  // 10が表示される
  println!("{}", x);
}
```

JavaScriptはこのような言語とは異なり、ブロックは文であり式ではありません。

# どちらが正しいのか
ここまでで、2種類の結果がそれぞれどのような解釈に基づいたものなのかを説明しました。では、どちらが正しくてどちらが間違っているのでしょうか。

一応述べておくと、最初に紹介した解釈では、`{a: 10}`は`{a: 10}`という式に`;`をつけた式文（ただし`;`が省略されている）と解釈されることになります。この解釈では、まず`{a: 10}`という式が評価され（結果は`{a: 10}`というオブジェクト）、その評価結果が式文の結果となったことから、`{a: 10}`という文を実行すると文の結果として`{a: 10}`というオブジェクトが表示されたことになります。

どちらが正しいのか確かめる一つの方法としては、[eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)を用いる方法があります。evalは、REPL以外で上で説明した「文の結果」を利用することができる唯一の方法です。

evalは引数としてJavaScriptプログラムの文字列を受け取り、それを実行する関数です。evalには式だけでなく文を渡すことができ、evalの返り値は最後に実行した文の（上で説明した意味での）結果となります。

```js:evalの例
var x = 10;
var res = eval('if (x > 0) { x * 10; } else { 0; }');
console.log(res); // 100
```

この例ではevalでif文を実行しました。if文の「文の結果」は、実行されたほうの文の結果となります。

evalで文の結果を取得できることが分かったので、問題の`{a: 10}`をevalに渡してみましょう。

```js
var res = eval('{a: 10}');
console.log(res); // 10
```

やってみると、結果は10となるはずです。ChromeなどはREPLで`{a: 10}`を実行すると`{a: 10}`になったにも関わらず、evalを使って試すと`10`になります。これはどうも`10`が優勢になってきましたね。

では、いよいよ**仕様書**を見ましょう。仕様書はJavaScript（ECMAScript）を定義する文書ですから、全ての答えが載っています。さらに言えば、JavaScriptの文法は曖昧性が無いことになっていますから、`{a: 10}`という文の解釈のどちらが正しくてどちらが間違っているか書いてあるはずです。

答えは、式文の定義を見ると載っています。

[![仕様書13.5節冒頭部分のスクリーンショット](https://gyazo.com/cb6f8433d7be32eb23f1005e316c5bdd/raw)](https://gyazo.com/cb6f8433d7be32eb23f1005e316c5bdd)

ご丁寧にNOTEの最初の一文に答えが書いてあります。

> An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block.
>
> （意訳）ブロックとの曖昧性を避けるため、式文は`{`で始まることができません。

というわけで、`{a: 10}`は最初が`{`なので、式文として解釈するのは間違いでした。この`{ }`はブロックとして解釈しなければならず、したがって`{a: 10}`の「文の結果」は`10`が正解です。

evalで`{a: 10}`を解釈したときにChromeの結果も`10`になったのはちゃんとこの仕様に従っているからということになります。

では、冒頭で紹介した、Chromeやnode.jsのREPLのこの挙動は間違いなのでしょうか。

[![Screenshot from Gyazo](https://gyazo.com/5131ae92d99137b5be85b76ac21da2f6/raw)](https://gyazo.com/5131ae92d99137b5be85b76ac21da2f6)

そう問われると、間違いとは言い切れないものがあります。なぜなら、**REPLの挙動は仕様で定められていない**からです。

先ほど仕様書からECMAScriptの文法の一部を引用しましたが、JavaScriptの文法はあくまでソース全体を一度にパースするときに適用されるものです。ですから、REPLのようにインタラクティブにプログラムが入力され部分部分を実行するような環境においても文法に厳密に従う必要がないという主張も道理が通るかもしれません。（といっても、本来のJavaScriptと乖離した解釈をされると何の役にも立ちませんから加減が必要ですが。）

結局のところ、なるべく仕様に厳密に従おうとしているのがFirefox等で、不必要な厳密さよりも利便性を優先したのがChromeやnode.jsということになります。

# 余談
## 曖昧性を無くす
`{a: 10}`をオブジェクトリテラルとして解釈して欲しい場合は`()`で囲むのが一番簡単です。`({a: 10})`とするとこれはブロックではなく式文として解釈されます。逆に`{a: 10}`をブロックとして解釈して欲しい場合は省略されているセミコロンを補って`{a: 10;}`とするとよいでしょう。

## アロー関数の`{ }`
`{ }`の意味が紛らわしいかもしれない場面として、アロー関数があるようです。アロー関数は関数を作るリテラルで、以下のように書きます。`{ }`の中に関数の本体が書かれていることが分かります。

```js:アロー関数の例
const func = (x, y) => {
  const answer = x + y;
  return answer;
};

console.log(func(3, 5)); // 8
```

また、アロー関数には`=>`の直後に返り値の式を書く省略形があります。

```js
const func = (x, y) => x + y;
// これは以下と同じ
// const func = (x, y) => { return x + y; };
```

では、こう書くとどうなるでしょうか。

```js
const func = (x, y) => { x + y };

console.log(func(3, 5)); // undefined
```

実は、上のように書くと`func`は返り値を返さない関数となります。なぜなら、アロー関数の形は`(引数たち) => 式`と`(引数たち) => { 文たち }`の2種類であり、後者の形の場合は中で明示的に`return`文を使わないと値を返せないからです。前者の形では`return`と書かなくても値を返せますが、その代わり書けるのは式だけです。

繰り返しになりますが、**ブロックは式ではなく文**なので、`=>`の後に`{ x + y }`と書いた場合は前者ではなく後者（中身は`x + y;`という式文）になります。

一応注意しておきますが、`x + y;`という式文の「文の結果」は`x+y`なのだから`x+y`が返り値として返らないとおかしい、などとは思わないでくださいね。文の結果はevalとREPL以外では無視されるのであり、勝手に関数の返り値になったりはしないのです。

## 仕様書を読む

さて、今回の記事、REPLにおける`{a: 10}`の問題とその答えをざっと理解していただけたのではないかと思います。しかし、読者の中には「お前の言っていることなど信用ならん、仕様書が全て！」という方もいるかもしれません。そのような人はQiitaの記事を読むべきではありませんが、もしかしたら居るかもしれないので、仕様書の中に今回の話題がどのように表れているかを最後に解説します。

仕様書を読むにあたってのキーワードは**Completion Record**です。Completion Recordは[仕様書の6.2.3節](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-completion-record-specification-type)で定義されており、ざっくり言うと「文や式の評価結果を表すオブジェクトのようなもの」です。もちろんこれは仕様書内の用語であり、JavaScriptプログラムに対して露出されることはありません。

Completion Recordは[[Type]]フィールドと[[Value]]フィールドを持ち[^target_field]、この2つによって計算結果が表現されます。[[Type]]フィールドは`normal`, `return`, `break`, `continue`, `throw`の5種類があり、[[Value]]は何らかのJavaScriptの値です。

[^target_field]: `break`や`continue`文の処理用に[[Target]]フィールドも持ちますが今回はあまり深入りしません。

普通に計算がされた場合は[[Type]]は`normal`となります。例えば`3 + 5`という式の計算結果を示すCompletion Recordは、なんやかんやがあった後で`{[[Type]]: normal, [[Value]]: 8}`となるでしょう。

[[Type]]が`return`であるようなCompletion Recordは、お察しとは思いますが`return`文の結果として現れます。他のタイプも同様です。

注目すべきは、Completion Recordは式の評価結果にも文の評価結果にも使われるという点です。これまたお察しかと思いますが、上で「文の結果という意味不明な概念」と呼んでいたのは、文の評価結果としてのCompletion Recordの[[Value]]フィールドのことです。

### if文の仕様

以上を踏まえて、仕様書を読んでみましょう。evalのときにちょっと話に出てきたif文を例にとります。まず、以下の文をREPLで実行してみてください。

```js
if (true) {
  100;
} else {
  10;
}
```

if文の「文の結果」として`100`が表示されるはずです。if文は条件部に応じてthen節かelse節のどちらかを実行するものですが、if文の「文の結果」は実行されたほうの文の結果と等しくなります。このことを仕様書で確かめましょう。該当部分の仕様はこのようになっています。

> [13.6.7 Runtime Semantics: Evaluation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-if-statement-runtime-semantics-evaluation)
IfStatement: `if` `(` Expression `)` Statement `else` Statement
>
1. Let exprRef be the result of evaluating Expression.
2. Let exprValue be ToBoolean(? GetValue(exprRef)).
3. If exprValue is true, then
&nbsp;&nbsp;&nbsp;&nbsp;a. Let stmtCompletion be the result of evaluating the first Statement.
4. Else,
&nbsp;&nbsp;&nbsp;&nbsp;a. Let stmtCompletion be the result of evaluating the second Statement.
5. Return Completion(UpdateEmpty(stmtCompletion, undefined)).

1は条件部分の式を評価します。ということは、その結果であるexprRefはCompletion Recordです……と言いたいところですが、実はそうとは限りません。式が`obj.foo`である場合など、Completion Recordではなく[Reference](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-reference-specification-type)である可能性があります。

2を順番に見ていきます。[GetValue](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-getvalue)は仕様書内でよく使われる便利関数であり、ざっくり言うとReferenceの中身を解決して値を取り出してくれます。例えば`obj.foo`という式を評価すると`obj.foo`を指すReferenceになりますが、GetValueを通すことで具体的な値に解決されます。Completion Recordの場合はそのままです[^normal_completion]。

[^normal_completion]: 厳密には[[Type]]が`normal`のCompletion Recordは取り外されて生の中身が返されますが、仕様書はどうも[[Type]]が`normal`のCompletionと生の値をあまり区別していないような気がしますので、ここでもそれに倣って無視できる場面では無視していきます。

次の`?`は[仕様書で超頻出のマクロです](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-returnifabrupt-shorthands)。これは前置マクロであり、「abrupt  completionのときはそれをこのアルゴリズム全体の結果とする。それ以外のときはそのまま」という意味です。Abrupt completionというのは、[[Type]]が`normal`以外のCompletion Recordのことです。

Abrupt completionの共通する挙動は「プログラムの実行をその場で中断して脱出する」というものです。これは、例外が発生した場合（[[Type]]が`throw`のCompletion Recordが発生）を考えるとイメージしやすいでしょう。この脱出という挙動が`?`によって表されています。要するに、2における`?`が言っていることは、「条件式の評価結果がabrupt completionだった場合は、if文の評価を中断してそれをそのままif文の結果とする」ということです。例外に例えると、条件式の評価中に例外が発生した場合、if文はそれ以上評価されずに例外が上へ伝播していく挙動を表しています。

次の`ToBoolean`は普通に値を真偽値に変換する関数です。`1`などの値はここで`true`に変換されます。

3と4は、条件式の結果に応じて1番目か2番目の文を実行し、その結果をstmtCompletionという変数に入れています。変数名から分かる通り、文を実行した結果はCompletion Recordです。

5は基本的には「その結果をそのままif文全体の結果とする」ということを言いたいわけですが、何か2つ関数をかませてあります。[UpdateEmpty](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-updateempty)は、「completion recordの[[Value]]が`empty`だったらそれをundefinedにする」というものです。この`empty`というのは「結果が無い」ということを表す厄介者です（また後で説明します）。その次のCompletionは、よく分かりませんが多分返り値がCompletion Recordであることを明示する以上の意味は無いです。

以上がif文のセマンティクスでした。「まず条件部分を評価し、その結果に応じてthen部分かelse部分かを評価し、その結果をif文全体の結果とする」という流れが読み取れたと思います。

### ブロックの仕様

他にも、ブロックの「文の結果」はその中の最後に実行された文の結果になると述べましたが、そのことは以下の部分で書かれています。

> [13.2.13 Runtime Semantics: Evaluation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-block-runtime-semantics-evaluation)
StatementList: StatementList StatementListItem
1. Let sl be the result of evaluating StatementList.
2. ReturnIfAbrupt(sl).
3. Let s be the result of evaluating StatementListItem.
4. Return Completion(UpdateEmpty(s, sl)).

ざっくり言うと、StatementListを前から順番に実行していって、abrupt completionが発生したら実行を中断してそれを返すようになっています。結果は`Completion(UpdateEmpty(s, sl))`となっていますが、これは結果は基本的には`s`（最後の文の結果）としつつも、もし結果が`empty`だったらその前の結果（`sl`）を採用するという意味です。

それ以外にも、while文やfor文は最後に実行したループの結果が採用される（`empty`は除く）などなかなか面白いことが書いてあるので、目を通してみるとよいのではないかと思います。尤も、REPLとevalでしか意味がないわけですが。

### `empty`
先ほども説明したとおり、Completion Recordの[[Value]]は`empty`である可能性があります。

具体的には、変数宣言（`let x = 1;`など）に加えて空文（`;`）、debugger文（`debugger;`）が該当します。また、ブロックについても、空の場合（`{ }`）や中の文の結果が全部`empty`の場合は該当します。上で説明した通り、`empty`はブロック文の結果に影響しない特性を持ちます。ただし、if文の例で見た通り、`empty`が好ましくない場合は基本的にundefinedに変換されます。

特に、変数宣言の「文の結果」が`empty`であることを用いると、REPLで変数を定義したときに`undefined`と表示される挙動が説明できます。

[![ChromeのREPLにlet a = 10;と入力したときのスクリーンショット](https://gyazo.com/d3ce093c50d14ee27f16cad60eabc320/raw)](https://gyazo.com/d3ce093c50d14ee27f16cad60eabc320)

ここで表示されているのは変数宣言の「文の結果」ですが、`empty`だったのでREPLでの表示のために`undefined`に変換されていると考えられます。（正直なところ、`empty`だったら表示しなくてもいいんじゃないかと個人的には思いますが。）

`empty`とブロックを組み合わせると楽しい挙動を観察できます。例えば、次のブロックの文の結果は`3`です。

```js
{
  3;
  var a;
}
```

その理由は、最後の文`var a;`の結果が`empty`なのでその前の文`3;`の結果である`3`が採用されているからです。

一方、次のブロックの文の結果は`undefined`です。

```js
{
  3;
  if (true) var a;
}
```

これは、`var a;`の文の結果は`empty`ですが、if文によりそれが`undefined`に変換されているため`if (true) var a;`の結果が`empty`ではなく`undefined`となっていることが理由です。

これにより、`文;`と`if (true) 文;`が等価ではないことが明らかになりました。（文の結果を考慮に入れるならですが。）

## `return` Completionを返す式
ところで、Completionの種類は`normal`, `continue`, `break`, `throw`, `return`の5種類でした。このうち、**式**の評価結果として可能なCompletionはどれでしょうか。

まず、普通の式は`normal`なのでこれは当然ありえます。また、式に評価中にエラーが発生することは普通にありますから、`throw`も可能です。

一方で、`continue`や`break`は無理でしょう。これらは`continue`文や`break`文により発生しますが、式の中からこれらの文に到達するには関数呼び出しによって別の関数に入る必要があります。しかし、`break`や`continue`の影響はその関数の中で完結してしまい、外に飛び出してくることはありません。

では、`return`はどうでしょう。これも`continue`や`break`と同じ理屈で無理のように思えます。別の関数に入ってその中で`return`文に到達しても、`return`文の効果はその関数から脱出した時点で終了します。では無理なのか、と思いきや**実は可能です**。`return` Completionを発生させることができる式が一つだけあります。それは**yield式**です。

yield式はジェネレータ関数の中でのみ使用可能な式であり、指定された値をジェネレータから発生させて実行を一時中断します。そして、ジェネレータの再開時にジェネレータに与えられた値を返します。

```js:ジェネレータの例
function* gen() {
  // まず1を発生させる
  const x = yield 1;
  // 次に10を発生させる
  const y = yield 10;
  // 最後に100を発生させて終了
  return 100;
}

// ジェネレータを作成
const g = gen();
// 1回目の値を発生させる
const value1 = g.next();
console.log(value1); // {done: false, value: 1}
// 2回目の値を発生させる
const value2 = g.next();
console.log(value2); // {done: false, value: 10}
// 3回目の値を発生させる
const value3 = g.next();
console.log(value3); // {done: true, value: 100}
```

このように、ジェネレータの`next`メソッドを呼ぶことで`yield`で止まっていたジェネレータ関数を先に進ませることができます。上の例では変数`x`と`y`に特に意味はありませんが、`yield`が式であることを強調するために書いています。

さて、実はジェネレータには`next`の他にあと2つメソッドがあります。それは`return`と`throw`です。なんと、returnメソッドを呼び出すことで、関数内部の`yield`から`return` Completionが発生するのです。

```js:ジェネレータをreturnさせる例
function* gen() {
  // まず1を発生させる
  const x = yield 1;
  // 次に10を発生させる
  const y = yield 10;
  // 最後に100を発生させて終了
  return 100;
}

// ジェネレータを作成
const g = gen();
// 1回目の値を発生させる
const value1 = g.next();
console.log(value1); // {done: false, value: 1}
// 強制returnさせる
const value2 = g.return(5);
console.log(value2); // {done: true, value: 5}
// もう実行は終了したのでnextを読んでも結果が来ない
const value3 = g.next();
console.log(value3); // {done: true, value: undefined}
```

この例では、2回目にreturnを呼んでいるので、`const y = yield 10;`が`return 5;`に書き換わったかのような挙動をします。`5`はreturnメソッドの引数に与えられたものです。

これが、式から`return` Completionを発生させる唯一の方法です。面白いですね。なお、もうひとつの`throw`メソッドというのはもちろん、`throw` Completionを発生させるメソッドです。

## do式
現在、ECMAScriptの新機能として[do式が提案されています](https://github.com/tc39/proposal-do-expressions)。といっても、アイデア段階のまま長らくStage 1なので難航しているようですが。

do式は`do { 文; ...; 文; }`のように`do`の中に文を並べることができるものですが、これ自体が式であるという点がポイントです。do式は、文たちを実行した「文の結果」を式の評価結果として取り出すことができるものなのです。

便利ですが、`if (true) 文`が`文`と同じではないみたいな問題が表面化してくることを考えるとなかなか大変なのもうなずけます。

# まとめ
余談が長くなりましたが、この記事では`{a: 10}`問題を取っ掛かりとして「文の結果」の概念について解説しました。

実用上この「文の結果」の概念はREPLくらいでしか関係してきません（最後に紹介したdo式が導入されればまた話が変わってきますが）。REPLで何か結果らしきものが現れたらそれがこの記事で説明した「文の結果」であることを理解し、正しくその意味を解釈できるようになりましょう。

特に、途中言及した**REPLの仕様は規定されていない**ことは重要です。REPLで「文の結果」とかいう変な概念が表面化することも相まって、**REPLの結果だけからJavaScriptについて議論することは危険**であることがお分かりになると思います。REPLの挙動を完璧に理解しているのでなければ、正確な結果が必要なときはREPLだけに頼るのではなく独立したJavaScriptプログラムを用意して実行してみるのがよいでしょう。

# 関連記事
- [{x: 1} は何と見なされるか —— JavaScriptコンソールによる違い](https://qiita.com/8x9/items/8e728e955aaad1d526ae) - node.jsにおける処理がソースコード付きで解説されている記事がありました。