ES2015(ES6) 初心者用Tutorialと途中で躓かないための補足まとめ

## ES6の勉強のチュートリアル
[こちら](http://ccoenraets.github.io/es6-tutorial/)のチュートリアルを使用します

### この記事の使い方

基本的に上記チュートリアルの補足的な位置づけです。上のチュートリアルをやりながら一緒にこっちも読むと途中で躓いたりすることも減るかと思います。

## 目的

React, Angular, Expressなど色々とフレームワークは出ていますが、そもそもベースとしてのES2015もちゃんと分かっとかないと、なんかコードが読めないとかこの環境構築どうなってんだみたいなことが起こりえます（例えばこのBabelとかpackage.jsonって何だろうとか）。じゃあTutorialをやろうと思っても、Tutorialが古くて、コピペしてもコードが思い通りに動かないといったことが発生します。そこで、この記事では自分がチュートリアルを実際に進めてこけた点をまとめ、後学の人の参考にできれば幸いです。

## 対象読者
- これからJavascript(ES6)を勉強しようという人
- ReactやAngularをいきなり始めたものの、何となくES6とごっちゃになっていて理解が進まない人
- Tutorialがなぜかうまく行かなくて先に進めない人

できればコマンドラインやエディタは使ったことのある方が好ましいです

## チュートリアル
![スクリーンショット 2018-12-09 19.58.51.png](https://qiita-image-store.s3.amazonaws.com/0/180185/214da4f9-8bf9-9c16-0565-578acf59f0b4.png)

ここのStart Hereから始めていきましょう

### 前準備
yarnをインストールしておきましょう。
[こちら](https://qiita.com/suisui654/items/1b89446e03991c7c2c3d)を参考にするとよいかと思います。yarnはfacebookが作ったjavascriptのパッケージマネージャになっています。

ちなみにTutorialは全てnpmというパッケージマネージャを使っていますが個人的にはyarnの方が良いです。
詳細は[こちら](https://ics.media/entry/19384)にまとまっていますが端折ると

* npmとほぼ同じ使い方
* インストールや実行がnpmと比べてかなり早い
* npmと共存しても問題ない

などの点があります。npmでできることはyarnでほぼできるので、そっちにしてしまった方がベターと思います。この記事でもyarnで解説します。


### Setting Up a Babel Project
さて、まずはプロジェクトのセットアップです。

```
git clone https://github.com/ccoenraets/es6-tutorial
```

ここからプロジェクトをダウンロードしましょう

次にプロジェクトの初期化をします。色々聞かれますが基本デフォルトでOKです。

```
$ yarn init
```

そこから、各種ライブラリをダウンロードします。

```
$ yarn add babel-cli babel-core --save-dev

$ yarn add babel-preset-es2015 --save-dev

$ yarn add http-server --save-dev
```

このbabel~というのがES6で書かれたJSをコンパイルするツールになっています。

ここで、プロジェクト直下にあるpackage.jsonを見てみましょう。
このファイルはyarnやnpmで初期化すると自動的に作成されます。ここの中でライブラリの管理(どんなライブラリが使われているか)や作者・ライセンスの管理を行います。

このpackage.jsonのdependenciesと同じ階層に以下を追記します

```json
"scripts": {
    "babel": "babel --presets es2015 js/main.js -o build/main.bundle.js",
    "start": "http-server"
},
```
このscriptの下に書いたものは全てコマンドのエイリアス扱いとなります。
例えば

```
$ yarn babel
```
を実行すると、下を実行したのと等価になります。

```
babel --presets es2015 js/main.js -o build/main.bundle.js
```

つまり、ここでのscriptを入れておくことで、babelコマンドでJSのビルド、startでhttp-serverの立ち上げができるようになります。
この設定、yarn buildでjs/main.jsをコンパイルしたものをbuild/main.bundle.jsとして出力しているので、buildディレクトリを作る必要があります。

```
$ mkdir build
```

試しにサンプルを実行してみましょう

```
$ yarn babel
$ yarn start
```

これで
http://localhost:8080
に接続できればOKです。

### Using let Variables

ここではlet変数について簡単に説明しています。var変数というのが今まで通りありますが、これはfunction scopeです。つまりよくある変数の定義でfunctionの内部で宣言した場合、その内部でのみ使える変数となります。

今回追加されたletはblock scopeの変数宣言となります。block　scopeというのは関数よりもさらに狭い範囲でif文などの中括弧内でのみ有効な変数の宣言方法です。このTutorialでは変数の宣言変えると元のコードが動かなくなるよ、という程度の説明ですが、基本的に頭のどこかに留めておけば十分かなと思います。

ちなみにこの例ではletですが、ES6ではconstも追加されました。こちらは定数を宣言したい場合に使うもので、この宣言をすると値の代入などが行えなくなります。

### Using Destructuring

分割代入(destructuring syntax)の説明です。概念としてはそんなに難しくなく、返り値に複数の値を持つ関数の書き方になります。

main.jsのcalculateMonthlyPaymentを

```javascript
return {principal, years, rate, monthlyPayment, monthlyRate};
```
のようにすると、それぞれの値が返却されます。

これは以下の省略形の書き方となります

```javascript
return { principal: principal, 
         years: years, 
         rate: rate, 
         monthlyPayment: monthlyPayment, 
         monthlyRate: monthlyRate };
```

そのため、一部を返したい場合にはその変数を宣言の右側に渡す必要があります。

```javascript
let {monthlyPayment, monthlyRate} = calculateMonthlyPayment(principal, years, rate);
```

実際はもう少し端折って、下記をcalculateMonthlyPaymentに追加すればOKです。構造体っぽいですね。

```javascript

var mortgage = calculateMonthlyPayment(principal, years, rate);
var monthlyPayment = mortgage.monthlyPayment;
var monthlyRate = mortgage.monthlyRate;
```

後はcalcBtnに以下を追加しましょう。id=monthlyRateを拾って値を書き換えます

```javascript
document.getElementById("monthlyRate").innerHTML = (monthlyRate * 100).toFixed(2);
```

html側に表示するためにindex.htmlにもMonthly Paymentの下の行に次を追加しましょう

```html
<h3>Monthly Rate: <span id="monthlyRate"></span></h3>
```

これでjsが反映されるかと思います。

二点注意事項として、もしこれでコードを書き換えてもうまく反映されない場合は以下を気をつけましょう。

* build忘れ
ES6はコンパイルが必要なので、毎回ビルドしないといけません。更新時にはyarn babelを忘れないようにしましょう。

* ブラウザにキャッシュが残っている
Chromeなどの場合、ブラウザに前のホームページのキャッシュが残っており、JSがうまく更新されない場合があります。その際はデベロッパーツール(右クリック検証)を開いて以下のDisable cacheを有効にしましょう。
![スクリーンショット 2018-12-09 16.58.17.png](https://qiita-image-store.s3.amazonaws.com/0/180185/7ba93108-ffee-7466-da1b-73171f3156b2.png)

### Using Arrow Functions
アロー関数の説明です。

```javascript
// 従来の書き方
var calculateMonthlyPayment = function (principal, years, rate) {...}

// アロー関数(とlet)を使った書き方
let calculateMonthlyPayment = (principal, years, rate) => {...}
```
arrowを使うことで、関数名をいちいち書く必要がなく、無名的に呼び出すことができるようになります。

引数がない場合も同様に活用できます

```javascript
// 従来の書き方
document.getElementById('calcBtn').addEventListener('click', function () {...}

// アロー関数を使った書き方
document.getElementById('calcBtn').addEventListener('click', () => {...}
```


### Setting Up Webpack

元々のbabelコマンドだと基本的に1つのJSファイルをビルドすることしかできないのですが、webpackを使用することで、複数のJSファイルを一つにまとめてビルドすることができます。詳しい概念は[こちら](https://qiita.com/soarflat/items/28bf799f7e0335b68186)を参照すると良いでしょう。

インストールは下記
```
$ yarn add babel-loader webpack --save-dev
```

package.jsonを以下のように修正する必要があります。

```
"scripts": {
    "babel": "babel --presets es2015 js/main.js -o build/main.bundle.js",
    "start": "http-server",
    "webpack": "webpack --mode development"
  },
```

このmodeというオプションはwebpack4で追加されたオプションでwebpack利用時にはこのオプションもつけることが推奨されています。このオプションを変えることでdevelopmentとproductionで別の設定を行ったりすることができます。

また、webpackのオプションを指定するために、ルートディレクトリ直下にwebpack.config.jsを作成する必要があります。

```javascript
var path = require('path');

module.exports = {
  entry: './js/main.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      }
    ]
  },
  stats: {
    colors: true
  },
  devtool: 'source-map'
};
```
ここで、元の記事ではloaderだったのが一箇所rulesとなっているので気をつけてください。

これで複数のJSファイルをyarn webpackでまとめてビルドできます。そのため、以後、yarn babelを使う必要はありません。

### Using Modules

はじめに元の記事にはないですが最初の時点でindex.htmlを下記に修正しましょう。

```html
<script type="module" src="js/main.js"></script>
```
scriptの部分にtypeが追加されています。これがないと、この章のimportなどの際にJS側で<font color="Crimson">Uncaught Error</font>が発生します。

別ファイルからJSのモジュールを呼び出す場合、呼び出し元にはexportを付ける必要があります。

```javascript
export let calculateMonthlyPayment = (principal, years, rate) => {...}
```

これを呼び出す際に、importを活用します。

```javascript
import * as mortgage from './mortgage';
```

余談ですが、mortgage(抵当)が個人的にとてもスペルミスしやすかたので気をつけてください。

### Using Classes

ES6になってJSでもクラスが楽に使えるようになりました。
constructorで呼び出し時の引数を内部　に渡して、getで外から呼び出す関数を定義できます。

```javascript
class Mortgage {
    
    constructor(principal, years, rate) {
        this.principal = principal;
        this.years = years;
        this.rate = rate;
    }
    
    get monthlyPayment() {
        let monthlyRate = this.rate / 100 / 12;
        return this.principal * monthlyRate / (1 - (Math.pow(1/(1 + monthlyRate),
                    this.years * 12)));
    }
    
    get amortization() {
        let monthlyPayment = this.monthlyPayment;
        let monthlyRate = this.rate / 100 / 12;
        let balance = this.principal;
        let amortization = [];
        for (let y=0; y<this.years; y++) {
            let interestY = 0;
            let principalY = 0;
            for (let m=0; m<12; m++) {
                let interestM = balance * monthlyRate;
                let principalM = monthlyPayment - interestM;
                interestY = interestY + interestM;
                principalY = principalY + principalM;
                balance = balance - principalM;
            }
            amortization.push({principalY, interestY, balance});
        }
        return amortization;
    }
    
}
```

こうしたクラスをモジュールとして別ファイルから呼び出す場合は

```javascript
export default class Mortgage {...}
```
のようにexport defaultが必要になります。

ちなみにここの例にあるようにcalcBtnを書き換える場合は元々id="amortization"となる要素がないのでindex.htmlで

```html
<div id="amortization"></div>
```
などを追加してください。

### Using Promises

ここでは非同期処理に使うPromiseの説明をしています。
Promiseは非同期処理に使うもので、外部APIの呼び出しなどレスポンスがいつ来るかわからない時に利用します。

例では、仮想のJSONリクエストを用意して

```json
let rates = [
    {
        "name": "30 years fixed",
        "rate": "13",
        "years": "30"
    },
    {
        "name": "20 years fixed",
        "rate": "2.8",
        "years": "20"
    }
];
```

下記のようなfindAll関数で内部のratesにjsonの値を格納しています

```javascript
export let findAll = () => new Promise((resolve, reject) => {
    if (rates) {
        resolve(rates);
    } else {
        reject("No rates");
    }
});
```

このfindAllを呼び出してthen、内部でratesを渡してその後の処理を書きます。最後のcatch部分はエラー処理になっています

```javascript
import * as service from './rate-service-mock';
    
service.findAll()
    .then(rates => {
        let html = '';
        rates.forEach(rate => html += `<tr><td>${rate.name}</td><td>${rate.years}</td><td>${rate.rate}%\</td></tr>`);
        document.getElementById("rates").innerHTML = html;
    })
    .catch(e => console.log(e));
```

## 最後に

ES6のTutorialを行ってみて、躓く点や古くなっている部分の補足や自分なりに学んだことも少し書きながらまとめました。このTutorial自体はかなりコンパクトなので、途中で変に躓かなければスムーズに読み進められるかと思います。

## 参考文献

[es6-tutorial](http://ccoenraets.github.io/es6-tutorial/)

[yarnをインストールする](https://qiita.com/suisui654/items/1b89446e03991c7c2c3d)

[webpack 4 入門](https://qiita.com/soarflat/items/28bf799f7e0335b68186)

[ES2015 (ES6)についてのまとめ](https://qiita.com/tuno-tky/items/74ca595a9232bcbcd727)