これから来そうなJavaScript新機能3選

時々[PHPのRFC](https://qiita.com/rana_kualu/items/644def9847b116c8f108)を紹介していますが、JavaScriptにも[proposal](https://github.com/tc39/proposals)という似たような仕組みが存在します。
JavaScript最大の進歩だった[async/await](https://qiita.com/rana_kualu/items/f3dfcb8e7ef0cc9955d6)も[proposal出身](https://github.com/tc39/ecmascript-asyncawait)です。

以下はそのproposalの中から、有用そうな提案3点を取り挙げた記事、[Here are three upcoming changes to JavaScript that you'll love](https://medium.freecodecamp.org/here-are-three-upcoming-changes-to-javascript-that-youll-love-387bce1bfb0b)の日本語訳です。

# Here are three upcoming changes to JavaScript that you'll love

今後来るであろうJavaScriptの便利な新機能を見てみましょう。
ここでは新しい構文の紹介、最新状況へのリンク、そして今すぐその構文を使ってみる小さなテストスイートを作成します。

## How JavaScript Changes

![0__ttLRUUBsYYoQS5u_.png](https://qiita-image-store.s3.amazonaws.com/0/26088/87803afc-0ed1-e6ef-72e8-7bc10a4d8b37.png)

[Ecma TC39](http://www.ecma-international.org/memento/TC39.htm)がどのようにJavaScriptの変更を管理しているかを既に知っている場合は、このセクションは飛ばしてもかまいません。

JavaScriptの言語仕様がどのように決定しているか知らない人のために、ここでプロセスの概要を簡単に説明します。

JavaScriptは、[ECMAScript](https://en.wikipedia.org/wiki/ECMAScript)という言語標準の実装です。
この標準は、Webブラウザの進化の初期段階において、JavaScriptの独自進化を[標準化する](https://en.wikipedia.org/wiki/Category:JavaScript_dialect_engines)ために形成されました。
ECMAScriptには8の[エディション](https://en.wikipedia.org/wiki/ECMAScript#Versions)が存在しており、7回のリリースが存在します(4番目のエディションは放棄された)。

各[JavaScriptエンジン](https://en.wikipedia.org/wiki/JavaScript_engine#JavaScript_engines)は、ECMAScriptがリリースされた後で、変更点の実装を開始します。
この[チャート](https://kangax.github.io/compat-table/es6/)を見るとわかるように、全てのJavaScriptエンジンが全ての機能を実装しているわけではなく、一部エンジンは機能を実装するのに非常に時間がかかることもあります。
これは最善ではないと思うかもしれませんが、それでも標準が存在しない世界よりは遙かに良いと私は考えています。

### Proposals

ECMAScriptの各エディションは、proposalによる審査のプロセスを経て公開されます。
proposalが有用で、かつ後方互換性を壊さないと見做された場合、それは次のエディションに追加されることになります。

proposalは[5段階のステージ](https://tc39.github.io/process-document/)に分かれています。
全てのproposalは、最初は"strawman"と呼ばれる[ステージ0](https://github.com/tc39/proposals/blob/master/stage-0-proposals.md)から始まります。
この段階のproposalは、まだ十分に検討されていないか、もしくは、次に進む基準を満たしていないしリジェクトもされてはいないという状態です。

ステージ0のproposalは今後どうなるか不安定で、破棄されたり大幅に変更されることもあります。
従って、それらをプロダクトアプリに使ったりしないことを強く勧めます。

ここで取り上げるproposalは、いずれもステージ0ではありません。

### Test Suite

プログラミングの紹介では、コードはしばしば抜粋した形で表示されます。
私は[TDD](https://en.wikipedia.org/wiki/Test-driven_development)が大好きなので、新機能を学ぶためにはテストを書いてみるのが一番だと思っています。

ここではKent Beckによる[学習テスト](https://smile.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530)を使用します。
このテストで書くアサーションは、自分で書いたコードをテストするというより、言語仕様自体を確認するという意味合いが強いです。
このコンセプトは、サードパーティのAPIや他の言語を学ぶときにも役立つ方法だと思います。

### Transpilers

既にトランスパイラを使っているのであれば、この章は飛ばしてもかまいません。

まだ実装されていない機能なのに、どうやって使用するのか不思議に思う人もいるかもしれません。

JavaScriptには、JavaScriptをJavaScriptに変換する[トランスパイラ](https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them)というものがいくつも存在します。
聞いただけではとても役に立ちそうには思えない気もしますが、しかしこれが実際にはとても役立つものだと私が保証します。
ステージ0のproposalsを含んだ最新バージョンのJavaScriptを書いたとしても、トランスパイラが、今のWebブラウザやNode.jsのような実行環境で動作するようにしてくれるのです。

これは、トランスパイラが[未実装のコードを古いバージョンでも動くJavaScriptに変換する](https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&code_lz=GYVwdgxgLglg9mABAEziARgGwKYGUCGAnogBQDOUATgJSIDeAUIopdlCJUhZYgNSIAiADSC-ibgG4GAXwYNQkWAkQR8ABxhR8mGAC9spbrUbNW7TuKoBtAAwBdAHRQ4AVTVrslAML4y2ErT83A5kGNwwYADmJACM1FKy8uDQ8EjYAB4QmPgwALaGVMZMLGwcXFRiAOQAhJUJcr6EkIgKKcpQ2BQB9EwA9L3i2AYAFnAA7oiaiLlwyDDAMJ2IUMMGjc2tSkh9A-ggUIg4-ABuSysGnQBMNjEArCpwuWr4sFgGm6lkiNoI2AD8MiAA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&lineWrap=false&presets=es2015%2Creact%2Cstage-0&prettier=false&targets=&version=6.26.0&envVersion=)ことによって実現されます。

現在最も普及しているJavaScriptのトランスパイラは[Babel](https://babeljs.io/)であり、以下の記事ではBabelを使用します。

### Setup

以下のコードを試してみるには、[Node.js](https://nodejs.org/en/)と[NPM](https://www.npmjs.com/)がインストールされている必要があります。
トランスパイラの導入は以下のコマンドで行います。

```bash
npm init -f && npm i ava@1.0.0-beta.3 @babel/preset-env@7.0.0-beta.42 @babel/preset-stage-0@7.0.0-beta.42 @babel/register@7.0.0-beta.42 @babel/polyfill@7.0.0-beta.42 @babel/plugin-transform-runtime@7.0.0-beta.42 @babel/runtime@7.0.0-beta.42 --save-dev
```

次に`package.json`に以下を追加します。

```json
	"scripts": {
	  "test": "ava"
	},
	"ava": {
	  "require": [
	    "@babel/register",
	    "@babel/polyfill"
	  ]  
	}
```

最後に`.babelrc`ファイルを作成します。

```json
	{
	  "presets": [
	    ["@babel/preset-env", {
	      "targets": {
	        "node": "current"
	      }
	    }],
	    "@babel/preset-stage-0"
	  ],
	  "plugins": [
	    "@babel/plugin-transform-runtime"
	  ]
	}
```

これで、テストを動かす準備が完了しました。

## 1. Optional Chaining

JavaScriptはObjectでできています。
Objectはしばしば、期待したような形になっていないことがあります。
以下は、データベースやAPIなどから取得したデータオブジェクトの例です。

```js
	const data = {
	  user: {
	    address: {
	      street: 'Pennsylvania Avenue',
	    }, 
	  },
	};
```

一方、データの登録を完了していないユーザがいたとしたらこうなります。

```js
	const data = {
	  user: {},
	};
```

何も考えずにstreetにアクセスしていた場合、以下のエラーが発生します。

```js
	console.log(data.user.address.street); // Uncaught TypeError: Cannot read property 'street' of undefined
```

現状では、エラーを防ぎつつstreetプロパティを確認するには、以下のようにアクセスしなければなりません。

```js
	const street = data && data.user && data.user.address && data.user.address.street;
	console.log(street); // undefined
```

私の感想としては、この方法は、
・醜悪
・厄介
・冗長
の三重苦です。

そこでOptional Chainingの登場です。

```js
	console.log(data.user?.address?.street); // undefined
```

素晴らしく簡単ですね。
この機能の有用性がわかったので、もっと詳しく見ていきましょう。
さっそくテストを書いてみます。

```js
	import test from 'ava';

	const valid = {
	  user: {
	    address: {
	      street: 'main street',
	    },
	  },
	};

	function getAddress(data) {
	  return data?.user?.address?.street;
	}

	test('存在すれば値を返す', (t) => {
	  const result = getAddress(valid);
	  t.is(result, 'main street');
	});
```

Optional Chainingは、`?`のつかないドット記法と互換を保っていることがわかります。
次に、存在しないプロパティを参照するテストを追加しましょう。

```js
	test('プロパティがなければundefinedが返ってくる', (t) => {
	  t.is(getAddress(), undefined);
	  t.is(getAddress(null), undefined);
	  t.is(getAddress({}), undefined);
	});
```

配列プロパティに対してのOptional Chainingの動作は以下のとおりです。

```js
	const valid = {
	  user: {
	    address: {
	      street: 'main street',
	      neighbors: [
	        'john doe',
	        'jane doe',
	      ],
	    },
	  },
	};

	function getNeighbor(data, number) {
	  return data?.user?.address?.neighbors?.[number];
	}

	test('Optional Chainingは配列プロパティも対象', (t) => {
	  t.is(getNeighbor(valid, 0), 'john doe');
	});

	test('配列プロパティがなければundefinedが返ってくる', (t) => {
	  t.is(getNeighbor({}, 0), undefined);
	});
```

Object内に関数が実装されているかどうかわからないことがあります。
よくある例はWebブラウザです。
古いブラウザには特定の機能が存在しない場合があります。
幸いなことに、関数の実装有無についてもOptional Chainingで検出可能です。

```js
	const data = {
	  user: {
	    address: {
	      street: 'main street',
	      neighbors: [
	        'john doe',
	        'jane doe',
	      ],
	    },
	    getNeighbors() {
	      return data.user.address.neighbors;
	    }
	  },
	};

	function getNeighbors(data) {
	  return data?.user?.getNeighbors?.();
	}

	test('Optional Chainingはfunctionも対象', (t) => {
	  const neighbors = getNeighbors(data);
	  t.is(neighbors.length, 2);
	  t.is(neighbors[0], 'john doe');
	});

	test('関数がなければundefinedが返ってくる', (t) => {
	  const neighbors = getNeighbors({});
	  t.is(neighbors, undefined);
	});
```

Optional Chainingで値を取得できなかった場合、そこで処理が止まります。
動作のイメージとしては概ね以下のようなかんじになります。

```js
	value == null ? value[some expression here]: undefined;
```

従って、オペレータ`?`がundefinedかnullを返した場合、それ以降の処理は実行されません。
以下のテストでその動作を確認できます。

```js
	let neighborCount = 0;

	function getNextNeighbor(neighbors) {
	  return neighbors?.[++neighborCount];
	}

	test('++neighborCountは一回しか呼ばれてない', (t) => {
	  const neighbors = getNeighbors(data);
	  t.is(getNextNeighbor(neighbors), 'jane doe');
	  t.is(getNextNeighbor(undefined), undefined);
	  t.is(neighborCount, 1);
	});
```

貴方は既にこの機能を使うことができます。
Optional Chainingを使うことで、余計なif文、lodashのようなライブラリ、`&&`を使わずに済むようになります。

### A word of warning

もしかしたら、Optional Chainingに多少のオーバーヘッドがあることに気付いたかもしれません。
`?`を過度に使用しすぎると、パフォーマンスが低下します。

Objectを作成したときか、受け取ったときにだけOptional Chainingを使って検証を行うことを勧めます。
それによって以後のチェックの必要がなくなり、パフォーマンス低下を抑えられます。

### Link

proposalは[ここ](https://github.com/TC39/proposal-optional-chaining)にあります。
この記事の一番下にもリンクの一覧を置いておきます。

## 2. Nullish coalescing

JavaScriptでよく見られる処理に以下のようなものがあります。
・値がundefinedかnullでないかをチェックする
・チェックに引っかかればデフォルト値を入れる
・ただし`0`、`false`、''などfalsyな値はそのまま通す

以下のようなコードになるでしょう。

```js
	value != null ? value : 'default value';
```

もしくは以下のように不適切な処理を見たことがあるかもしれません。

```js
	value || 'default value'
```

こう書いてしまうと、`0`や`false`、''なども全てfalseと見做されてしまい、デフォルト値が入ってしまいます。
そのため、値は明示的にnullと比較しなければなりません。

```js
	value != null
```

これは以下と同じです。

```js
	value !== null && value !== undefined
```

ここで新たなオペレータ、Nullish coalescingの登場です。

```js
	value ?? 'default value';
```

これは、falsyな値をうっかりデフォルト値で上書きすることを防ぎつつ、三項演算子と`!= null`を使わずにnullとundefinedをチェックすることができます。
構文がわかったので、実際にどう動くかテストを書いてみます。

```js
	import test from 'ava';

	test('nullは該当', (t) => {
	  t.is(null ?? 'default', 'default');
	});

	test('undefinedも該当', (t) => {
	  t.is(undefined ?? 'default', 'default');
	});

	test('void 0も該当', (t) => {
	  t.is(void 0 ?? 'default', 'default');
	});

	test('0はNullish coalescingではない', (t) => {
	  t.is(0 ?? 'default', 0);
	});

	test('空文字もNullish coalescingではない', (t) => {
	  t.is('' ?? 'default', '');
	});

	test('falseもNullish coalescingではない', (t) => {
	  t.is(false ?? 'default', false);
	});
```

null、undefined、および`void 0`がNullish coalescingにひっかかることがわかります。
逆に0や''、falseなどfalsyな値では引っかかりません。
なお`void 0`はundefinedと評価されます。

proposalは[こちら](https://github.com/tc39/proposal-nullish-coalescing)です。

## 3. Pipeline operator

関数型プログラミングには複数の関数を合成する[Composition](https://medium.com/javascript-scene/composing-software-an-introduction-27b72500d6ea)という機能があります。
各関数は手前の関数の出力を入力として受け取ります。
プレーンなJavaScriptでの例を以下に示します。

```js
	function doubleSay (str) {
	  return str + ", " + str;
	}
	function capitalize (str) {
	  return str[0].toUpperCase() + str.substring(1);
	}
	function exclaim (str) {
	  return str + '!';
	}
	let result = exclaim(capitalize(doubleSay("hello")));
	result //=> "Hello, hello!"
```

これらの書き方はとても一般的なため、[lodash](https://lodash.com/docs/4.17.5#flow)や[ramda](http://ramdajs.com/docs/#compose)をはじめ多くのライブラリでサポートされています。

Pipeline operatorを使うと、サードパーティライブラリを使わず以下のように書けるようになります。

```js
	let result = "hello"
	  |> doubleSay
	  |> capitalize
	  |> exclaim;

	result //=> "Hello, hello!"
```

これの目的は、関数のチェーンをより読みやすくするためです。

将来は部分適用もできるようになる予定ですが、現在は以下のように確認できます。

```js
	let result = 1
	  |> (_ => Math.max(0, _));

	result //=> 1
	let result = -5
	  |> (_ => Math.max(0, _));

	result //=> 0
```

シンタックスがわかったのでテストを書いてみます。

```js
	import test from 'ava';

	function doubleSay (str) {
	  return str + ", " + str;
	}

	function capitalize (str) {
	  return str[0].toUpperCase() + str.substring(1);
	}

	function exclaim (str) {
	  return str + '!';
	}

	test('Simple pipeline usage', (t) => {
	  let result = "hello"
	    |> doubleSay
	    |> capitalize
	    |> exclaim;

	  t.is(result, 'Hello, hello!');
	});

	test('Partial application pipeline', (t) => {
	  let result = -5
	    |> (_ => Math.max(0, _));

	  t.is(result, 0);
	});

	test('Async pipeline', async (t) => {
	  const asyncAdd = (number) => Promise.resolve(number + 5);
	  const subtractOne = (num1) => num1 - 1;
	  const result = 10
	    |> asyncAdd
	    |> (async (num) => subtractOne(await num));
	  
	  t.is(await result, 14);
	});
```

ひとつ注意点としては、Pipeline operatorで非同期関数を使う場合はawaitしなければならないことです。
そうしないと、値がPromiseになってしまうからです。
非同期関数をうまく扱う`|> await`のような文法がいくつか[提案](https://github.com/tc39/proposal-pipeline-operator)されていますが、いずれも未実装か未決定です。

さて、ここまで読んできたことで、これらの機能を使いこなすことができるようになったことでしょう。
これらの機能を使って快適なプログラミングができることを信じています。

## The full code

全てのテストコードは[こちら](https://github.com/JustinDFuller/javascript-proposals-tests)で見付けることができます。

[TC39/proposal-optional-chaining](https://github.com/TC39/proposal-optional-chaining)
[TC39/proposal-nullish-coalescing](https://github.com/tc39/proposal-nullish-coalescing)
[TC39/proposal-partial-application](https://github.com/tc39/proposal-partial-application)
[TC39/proposal-pipeline-operator](https://github.com/tc39/proposal-pipeline-operator)


# 感想

PHPの[@](http://php.net/manual/ja/language.operators.errorcontrol.php)を[叩かなかった人](https://www.google.co.jp/search?q=エラー制御演算子)だけがOptional Chainingを賞賛するがよい。

今回は英文がかなりわからなかった。
「If you want to follow along with the code then feel free.」わからん。
「Each level that you check with ? must be wrapped in some sort of conditional logic under the hood. 」わからん。