古き良きJSをES2015でリファクタリング

ES2015がリリースされてから、すでに約2年半が経過しようとしています。
皆さんはES2015の書き方には慣れてきたでしょうか？

今更感はありますが、今回はES2015以前のJSをES2015を使ってリファクタリングしてみようと思います。

ES2016/ES2017については、~~面倒だったので~~今回は割愛させて頂きます。

## リファクタリング対象
今回リファクタリングしていく対象のソースコードです。

```js
// Personクラスを定義
var Person = function(lastName, firstName) {
  this.lastName = lastName || '山田';
  this.firstName = firstName || '太郎';
}

// hello()メソッドを定義
Person.prototype.hello = function() {
  return '私の名前は'+this.firstName+'です。';
}

// 引数で受け取った人物を紹介する関数
var introduce = function(persons) {
  var length = persons.length;
  for(var i = 0; i < length; i++) {
    var person = persons[i];
    var fullName  = person.lastName + person.firstName;
    console.log('あの人は'+fullName+'です。');
  }

  console.log('最後の紹介した人は'+fullName+'だよ。');
}

// Personのインスタンスを生成
var personA = new Person('小早川', 'セナ');
var personB = new Person('進', '清十郎');

// 私の名前はセナです。
console.log(personA.hello());

// あの人は小早川セナです。
// あの人は進清十郎です。
// 最後に紹介した人は進清十郎だよ
introduce([personA, personB])
```

## class構文
プロトタイプの記述をclass構文を使って変更していきます。
散々、苦しめられた`prototype`の記述が無くなり早速モダンな感じになりました。

※ class構文は、あくまでプロトタイプベースの糖衣構文です。

```js:変更前
var Person = function(lastName, firstName) {
  this.lastName = lastName || '山田';
  this.firstName = firstName || '太郎';
}

Person.prototype.hello = function() {
  return '私の名前は'+this.firstName+'です。';
}
```

```js:変更後
class Person {
  constructor(lastName, firstName) {
    this.lastName  = lastName  || '山田';
    this.firstName = firstName || '太郎';
  }

  hello() {
    return '私の名前は'+this.firstName+'です。';
  }
}
```

## デフォルト引数
デフォルト引数を利用することで、`undefined`のチェックが不要になりスッキリと書けるようになりました。

```js:変更前
class Person {
  constructor(firstName, lastName) {
    this.lastName  = lastName  || '山田';
    this.firstName = firstName || '太郎';
  }

  ...
}
```

```js:変更後
class Person {
  constructor(firstName = '山田', lastName = '太郎') {
    this.lastName  = lastName;
    this.firstName = firstName;
  }

  ...
}
```

## テンプレート文字列
``` `` ```（バッククォート）で文字列を囲むことで変数展開が出来ます。
展開する変数は`${}`で囲みます。

文字列結合の記述が簡素になり非常に見やすくなりました。


```js:変更前
  hello() {
    return '私の名前は'+this.firstName+'です。';
  }
```

```js:変更後
  hello() {
    return `私の名前は${this.firstName}です。`
  }
```

## アロー関数
アロー関数を使うことで、`function式`よりも短い構文で記述が可能です。
引数が一つの時に限り`()`は省略できます。

```js:変更前
var introduce = function(persons) {
   ...
}
```

```js:変更後
// 引数が一つなので
// var introduce = persons => {}
// と記述もできる
var introduce = (persons) => {
  ...
}
```

アロー関数は`this`を束縛しないので、jQueryなどを使う場合は注意してください。

```js
// 誤った書き方
$('button').on('click', () => {
　　　　// [window]
  // thisは束縛されないので、windowオブジェクトを指す
  console.log($(this));
});

// 正しい書き方
$('button').on('click', function() {
　　　　// [button]
  // thisは束縛されるので、jQueryオブジェクトを指す
  console.log($(this));
});
```

## let, constによる変数宣言
ES2015では新たな変数宣言のキーワードとして`let`,`const`が導入されました。
`let`はブロックスコープによる変数宣言が可能になっており、if文の`{}`の中だけで有効な変数を宣言できます。
`const`は再代入が不可能な変数（定数）を宣言できます。

```js:変更前
var introduce = (persons) => {
  var length = persons.length;
  for(var i = 0; i < length; i++) {
    var person = persons[i];
    var fullName  = person.lastName + person.firstName;
    console.log(`あの人は${fullName}です。`);
  }

  // fullName は最後のループの文字列になる
  // var は関数スコープのため fullName は参照可能
  console.log(`最後の紹介した人は${fullName}だよ。`);
}
```

```js:変更後
const introduce = (persons) => {
  const length = persons.length;
  for(let i = 0; i < length; i++) {
    let person = persons[i];
    let fullName  = person.lastName + person.firstName;
    console.log(`あの人は${fullName}です。`);
  }

  // fullName はundefinedになる
  // let はブロックスコープのため fullName は参照出来ない
  console.log(`最後の紹介した人は${fullName}だよ。`);
}
```

## リファクタリング結果
結果として見ると、そこまで大きく変わってないように見えませんか？
ES2015で書くと言っても、書き方の構造を大きく変更する訳では無いので、全く新しいものと抵抗を感じる必要は無いと思います。

```js
class Person {
  constructor(firstName = '山田', lastName = '太郎') {
    this.lastName  = lastName;
    this.firstName = firstName;
  }

  hello() {
    return `私の名前は${this.firstName}です。`
  }
}

const introduce = (persons) => {
  const length = persons.length;
  for(let i = 0; i < length; i++) {
    let person = persons[i];
    let fullName  = person.lastName + person.firstName;
    console.log(`あの人は${fullName}です。`);
  }

  console.log(`最後の紹介した人は${fullName}だよ。`);
}

const personA = new Person('小早川', 'セナ');
const personB = new Person('進', '清十郎');

// 私の名前はセナです。
console.log(personA.hello());

// あの人は小早川セナです。
// あの人は進清十郎です。
// "ReferenceError: fullName is not defined
introduce([personA, personB])
```

## さいごに
今回、紹介出来なかった機能として[分割代入](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)や[Promise](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)など、ES2015には他にも便利な機能が沢山あります。

また、ES2016/ES2017にも[Array.prototype.includes()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)や[AsyncFunction](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction)などの便利な機能があるので、慣れてきたら使ってみてください。