ES2018で追加される機能まとめ

## はじめに
> ECMAScript 2018に最終的に入るProposalが決定しました。
> ECMAScriptは毎年6月ごろにリリースされるため、これから新しい機能(Proposal)を入れると間に合わなくなります。
> そのため、1月ごろに機能(Proposal)の追加はフリーズされ、この時期からStage 4となったProposalはECMAScript仕様本体へマージされます。
>
> 2018-01-29のJS: Firefox 58、Safari 11.1(β)、Parcel 1.5.0、webpack 4(β)、ES 2018の機能が決定 - JSer.info
> https://jser.info/2018/01/30/firefox-58-safari-11.1b-parcel-1.5.0-webpack-4b-es-2018/

というわけで、ES2018のProposalがまとまった模様です。
さっそく追ってみます。

## テンプレートリテラルのエスケープシーケンス見直し
ES6で追加されたテンプレートリテラルの、バックスラッシュに関するエラーの見直しが図られています。
普通に使っているとそこまでエラーには遭遇しないかもしれませんが、以下のようなケースでエラーになっているみたいです。

```javascript
const texCommand = `\usepackage[utf8]{inputenc}`;
// Uncaught SyntaxError: Invalid Unicode escape sequence
```

`\u`の部分がUnicodeエスケープシーケンス（例：\u0020）の開始と解釈されますが、不正なエスケープシーケンスとしてエラーを吐いてしまいます。今後はこれをエラーにせず、`undefined`として扱うみたいです。

## `Promise`に`finally()`が追加
`Promise`の必ず最後に実行される`finally()`メソッドが追加されました。
`try-catch-finally`のように`resolve`や`reject`の状態に関わらず、必ず最後に実行されるものとして使用可能です。  
たとえばajax時のローダーを非表示にする処理などはこちらに書くのが適しているといえます。

```javascript
new Promise((resolve, reject) => {
  window.setTimeout(resolve, 100)
}).then(() => {
  console.log('then')
}).catch(e => {
  console.log('catch')
}).finally(() => {
  console.log('finally')
})
// then
// finally
```
```javascript
new Promise((resolve, reject) => {
  window.setTimeout(reject, 100)
}).then(() => {
  console.log('then')
}).catch(e => {
  console.log('catch')
}).finally(() => {
  console.log('finally')
})
// catch
// finally
```

## スプレッド演算子がオブジェクトにも使用できるように
ES6で追加されたスプレッド演算子ですが、配列だけでなくオブジェクトにも使用できるようになります。

```javascript
/* Rest Properties */
const { a, b, ...c } = { a: 1, b: 2, x: 3, y: 4, z: 5}
console.log(a)  // 1
console.log(b)  // 2
console.log(c)  // {x: 3, y: 4, z: 5}

/* Spread Properties */
console.log({ a, b, ...c })
// {a: 1, b: 2, x: 3, y: 4, z: 5}
```

## 非同期に対応したイテレーターの追加
ES6で実装された`Iterator`は同期データの取得に対してとても役に立つ機能でした。  
ES2018では非同期データにも対応できるようになりました。
それに伴い、プロパティを操作する`for-of`とは別に`for-await-of`が実装されました。

```javascript
for await (const line of readLines(filePath)) {
  console.log(line);
}
```

たとえば以下のコードでは、
`a()`の中で`for-of`を使用してURLを走査し、`fetch()`してStringを返します。
`b()`の中で`for-await-of`を使用して、`fetch()`した内容を出力するようにしています。

```javascript
const urls = [
  'https://github.com/tc39/proposal-object-rest-spread',
  'https://github.com/tc39/proposal-regexp-lookbehind',
  'https://github.com/tc39/proposal-regexp-unicode-property-escapes',
  'https://github.com/tc39/proposal-promise-finally',
  'https://github.com/tc39/proposal-async-iteration'
]

async function * a () {
  for (const url of urls) {
    console.log(`Fetching: ${url}`)
    const response = await fetch(url)
    const iterable = response.text()
    yield iterable
  }
}

async function b () {
  for await (const i of a()) {
    const title = i.match(/<title>(.+)<\/title>/)[1]
    console.log(`Title: ${title}`)
  }
}

b();

// Fetching: https://github.com/tc39/proposal-object-rest-spread
// Title: GitHub - tc39/proposal-object-rest-spread: Rest/Spread Properties for ECMAScript
// Fetching: https://github.com/tc39/proposal-regexp-lookbehind
// Title: GitHub - tc39/proposal-regexp-lookbehind: RegExp lookbehind assertions
// Fetching: https://github.com/tc39/proposal-regexp-unicode-property-escapes
// Title: GitHub - tc39/proposal-regexp-unicode-property-escapes: Proposal to add Unicode property escapes `\p{…}` and `\P{…}` to regular expressions in ECMAScript.
// Fetching: https://github.com/tc39/proposal-promise-finally
// Title: GitHub - tc39/proposal-promise-finally: ECMAScript Proposal, specs, and reference implementation for Promise.prototype.finally
// Fetching: https://github.com/tc39/proposal-async-iteration
// Title: GitHub - tc39/proposal-async-iteration: Asynchronous iteration for JavaScript
```
このコードはとてもわかりやすかったため、以下のページから抜粋しています。
> ECMAScript 2018（ES2018）の新機能まとめ | あるいてっく
> https://arui.tech/es2018-new-features/#Lookbehind_Assertions

## 正規表現
ここから下は全て正規表現の機能です。

### `s`フラグの追加
正規表現で使用される`.`は改行コードなどを含まれていませんでした。
これまでこの問題を回避するためには`[\s\S]`のように書く必要がありました。

```javascript
/foo.bar/.test('foo\nbar');
// → false

/foo[\s\S]bar/.test('foo\nbar');
// → true
```

少し分かりづらかったこの問題を回避するために`s`フラグが追加されました。  
`s`フラグを使用することで正規表現中に含まれる`.`が改行コードなどを含めて評価してくれるようになります。

```javascript
/foo.bar/s.test('foo\nbar');
// → true
```

### 正規表現のキャプチャグループに名前がつけられるように変更
今まで`exec()`などでキャプチャした場合、結果として配列が返ってきていました。
この配列は`result[0]`のように添え字を渡すことでしか判断できませんでした。

```javascript
let re = /(\d{4})-(\d{2})-(\d{2})/;
let result = re.exec('2015-01-02'); 
// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
```

この問題を回避するために、キャプチャする部分に名前を渡すことができるようになりました。
使い方は簡単で`(?<name>...)`のシンタックスで書けばOKです。

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
```
#### 後方参照が追加
この機能には後方参照というものがあり、`\k<name>`を使うと、前方で使用した名前に再度アクセスすることができます。
言葉にするとわかりづらいため、以下のコードを御覧ください。

```javascript
let duplicate = /^(?<half>.*).\k<half>$/u;
duplicate.test('a*b'); // false
duplicate.test('a*a'); // true
```

#### `replace()`での使用
`replace()`でもこの機能は使用することができます。  
例えば以下のコードは日付のフォーマットを入れ替えるような動作をします。

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = '2015-01-02'.replace(re, '$<day>/$<month>/$<year>');
// result === '02/01/2015'
```

### 後読みの追加
javascriptの正規表現には先読みしか存在しておらず、今まで後読みのアサーションが存在していませんでした。
今回ついに後読みアサーションが実装されることになりました。
先読みと後読みそれぞれを記載いたします。

#### 肯定先読み
まず先読みは`(?=***)`のシンタックスで書きます。
以下のコードは`123`に`456`が続くかどうかを判断しています。

```javascript
'123456'.match(/123(?=456)/)     // -> ["123"]
'123'.match(/123(?=456)/)        // -> null
'1234'.match(/123(?=456)/)       // -> null
'1234567'.match(/123(?=456)/)    // -> ["123"]
```

#### 否定先読み
否定先読みは`(?!***)`のシンタックスで書きます。

```javascript
'123456'.match(/123(?!456)/)     // -> null
'123'.match(/123(?!456)/)        // -> ["123"]
'1234'.match(/123(?!456)/)       // -> ["123"]
'1234567'.match(/123(?!456)/)    // -> null
```

#### 肯定後読み
今回追加された後読みは`(?<=***)`のシンタックスを使います。
以下のコードは、上に書いたコードの逆で`123`を前にもつ`456`を検出します。

```javascript
'123456'.match(/(?<=123)456/)     // -> ["456"]
'456'.match(/(?<=123)456/)        // -> null
'3456'.match(/(?<=123)456/)       // -> null
'1234567'.match(/(?<=123)456/)    // -> ["456"]
```

#### 否定後読み
否定後読みは`(?<!***)`のシンタックスで表されます。
そのため、以下のコードでは`123`を持たない`456`を検出します。

```javascript
'123456'.match(/(?<!123)456/)     // -> null
'456'.match(/(?<!123)456/)        // -> ["456"]
'3456'.match(/(?<!123)456/)       // -> ["456"]
'1234567'.match(/(?<!123)456/)    // -> null
```

### ユニコード内のプロパティがマッチング対象に追加
Unicodeの文字内にはたくさんのプロパティが指定されています。  
このプロパティを、正規表現の中でマッチングに使うことができるようになります。  
今までは同様の機能を実装するのに外部ライブラリなどを使用する必要があったのですが、今回は言語仕様としてサポートされることになりました。

この機能は`\p{…}` and `\P{…}`のシンタックスを使って書きます。

```javascript
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π');
// → true
```
Unicodeプロパティの調べ方などは以下のページにも詳しくまとまっていました。
> ECMAScript 2018（ES2018）の新機能まとめ | あるいてっく
> https://arui.tech/es2018-new-features/#Lookbehind_Assertions

## おわりに
正規表現に多くの機能が追加され、細かいところが強化されている印象を受けました。
何かと使うことが多い機能なので上手に使いこなしていきたいと思います。