今日から始める ES2015(ES6)


## ECMAScript とは

えくますくりぷと。
JavaScript の言語仕様。Ecma Internationl によって標準化手続きが行われている。
ECMAScript をもとに実装されている他の言語には ActionScript や JScript などがある。



## ES2015(ES6) とは

**ECMAScript 6th Edition** のこと。
2015年に仕様策定されたため、正式名称を **ECMAScript 2015**(ES2015) とし、今後は年単位での細かいリリースが計画されている。

現在、一般的に利用されている JavaScript は ES5 を元にしており、ES2015 は次世代の JavaScript という位置づけになる。
複雑で高機能化するWebアプリケーションの開発に応えるため、ES2015 では様々な機能追加や修正が行われている。



## ES2015 で追加された主な機能

### `let`, `const` による変数宣言

* `let` は再宣言が不可能な変数宣言
* `const` は再宣言と再代入が不可能な変数宣言

```js:ES5.js
var val = 'variable1';
console.log(variable1); // variable1

val = 'variable2'; // 再代入
console.log(val);  // variable2

var val = 'variable3'; // 再宣言
console.log(val);      // variable3

```

```js:ES2015.js
let val = 'variable1';
console.log(variable1); // variable1

val = 'variable2'; // 再代入
console.log(val);  // variable2

let val = 'variable3'; // 再宣言でエラー
```

```js:ES2015.js
const val = 'variable1';
console.log(variable1); // variable1

val = 'variable2'; // 再代入でエラー
```

#### ブロックスコープ

`{}`（ブラケット）によるブロックスコープが有効になる（スタンドアロンのブラケットでもブロックが生成される）。
ブロック内で`let`, `const`を利用して宣言された変数のスコープは、そのブロック内に絞られる。

```js:ES2015.js
{
  var val1 = 1;
  let val2 = 2;
  const val3 = 3;
  
  console.log(val1); // 1
  console.log(val2); // 2
  console.log(val3); // 3
}

console.log(val1); // 1
console.log(val2); // undefined
console.log(val3); // undefined
```


### テンプレートリテラル

`` ` ` ``（バッククォート）で囲われた文字列内では、`${}`で変数展開ができる。改行も反映される。

```js:ES5.js
var version = 'ES5';

console.log('JS version is ' + version); // JS version is ES5
```

```js:ES2015.js
const version = 'ES2015';

console.log(`JS version is ${version}`); // JS version is ES2015
console.log(`JS
version
is
${version}`); // JS
              // version
              // is
              // ES2015
```


### `for...of`

反復可能なオブジェクトに対する処理が、より直感的にできるようになった。

```js:ES5.js
var array = [65, 55, 80, 100];

for(var index in array) {
  console.log(index, array[index]);
}
// 0 65
// 1 55
// 2 80
// 3 100

array.forEach(function (value, index) {
  console.log(index, value);
});
// 0 65
// 1 55
// 2 80
// 3 100

var obj = {
  country: 'Japan',
  language: 'Japanese'
};

for(var key in obj) {
  console.log(key, obj[key]);
}
// country Japan
// language Japanese
```

```js:ES2015.js
const array = [65, 55, 80, 100];

for(const value of array) {
  console.log(value);
}
// 65
// 55
// 80
// 100

// for...of ではオブジェクトのプロパティ値は処理できないので、従来通りのやり方で
```

### アロー関数

従来の`function`より簡素な記述で関数が定義できる。

```js:ES5.js
// 従来の function を使った書き方
var plus = function(x, y) {
  return x + y;
};
```

```js:ES2015.js 
// アロー関数で function を省略
let plus = (x, y) => {
  return x + y;
};
 
// 単一式の場合はブラケットや return を省略できる
let plus = (x, y) => x + y;

// 引数が1つの場合は、仮引数の () も省略できる
let square = num => num * num;
```

#### `this` の束縛

従来の`this`は実行時の呼び出し元に紐付けられていたが、アロー関数では関数宣言時の`this`スコープを引き継ぐため、`bind(this)`で`this`を束縛したり、self, that, _this などの別変数で`this`を退避する必要がなくなった。

```js:ES5
var counter = {
  count: 0,

  start: function() {
    // thisを別名で退避
    var self = this;

    setTimeout(function() {
      // this.count++;
      // 実行時の呼び出し元は window オブジェクトであり、
      // この場合での this は counter オブジェクトではないため、
      // 退避した変数 self を使用する必要がある
      
      self.count++;
    }, 1000);
  }
};

```

```js:ES2015.js
var counter = {
  count: 0,

  start: function() {
    setTimeout(() => {
      this.count++;
    }, 1000);
  }
};
```


### デフォルト引数

関数の引数に省略時のデフォルト値が設定できる。

```js:ES2015.js
function square (num = 5) {
  return num * num;
}

console.log(square());   //  25
console.log(square(10)); // 100
```

### スプレッド演算子 `...`

式が置かれるところで値が展開される。

#### 可変長引数（レストパラメータ）

関数の仮引数にスプレッド演算子を利用すると、可変長引数を配列で受け取ることができる。

```js:ES2015.js
function func1 (...params) {
  console.log(params);
  console.log(params[0]);
}

func1(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
                      // 1

function func2 (val, ...params) {
  console.log(val);
  console.log(params);
}

func2(1, 2, 3, 4, 5); // 1
                      // [2, 3, 4, 5]
```


#### 配列展開

スプレッド演算子を利用すると、配列の展開ができる。

```js:ES5.js
var array = [1, 10, 3];

console.log(array);                       // [1, 10, 3]
console.log(Math.max(1, 10, 3));          // 10
console.log(Math.max(array));             // NaN
console.log(Math.max.apply(null, array)); // 10
                                          // apply() は、第2引数の配列の中身が引数として渡される
```

```js:ES2015.js
const array = [1, 10, 3];

console.log(...array);            // 1 10 3
console.log(Math.max(1, 10, 3));  // 10
console.log(Math.max(array));     // NaN
console.log(Math.max(...array)); // 10

function func (x, y, z) {
  console.log(x);
  console.log(y);
  console.log(z);
}

func(...array); // 1
                // 10
                // 3
```

#### 分割代入

分割代入時にスプレッド演算子を利用すると、複数の値を1つの配列にまとめられる。

```js:ES2015.js
const [a, b, ...c] = ['a', 'b', 'c', 'C'];

console.log(a); // a
console.log(b); // b
console.log(c); // ['c', 'C']
```

#### 配列の結合

スプレッド演算子を利用すると、配列の結合が簡単にできる。

```js:ES5.js
var array = [1, 2, 3];
var array2 = array.concat([4, 5, 6]);

console.log(array2); // [1, 2, 3, 4, 5, 6]
```

```js:ES2015.js
const array = [1, 2, 3];
const array2 = [...array, 4, 5, 6];

console.log(array2); // [1, 2, 3, 4, 5, 6]
```

### `class` によるクラス宣言

従来の prototype ベースの構文より、簡潔で馴染みのある記述でクラスを定義できる。

```js:ES2015.js
// 従来の prototype ベースの構文
var Person = function(name) {
  this.name = name;
};

Person.prototype.getName = function() {
  return this.name;
};

Person.prototype.greeting = function() {
  console.log(`Hello, I am ${this.getName()}`);
};

// Class構文
class Person {
  constructor(name) {
    this.name = name;
  }
  
  getName() {
    return this.name;
  }
  
  greeting() {
    console.log(`Hello, I am ${this.getName()}`);
  }
}
```

#### `extends` を利用したクラスの継承

```js:ES2015.js
class Person {
  constructor(name) {
    this.name = name;
  }
  
  getName() {
    return this.name;
  }
  
  greeting() {
    console.log(`Hello, I am ${this.getName()}`);
  }
}

// Person を継承
class Man extends Person {
  greeting() {
    console.log(`Hi, I am ${this.getName()}`);   
  }
}

const man = new Man('Bob');
man. greeting(); // Hi, I am Bob
```


### その他、ES2015 で追加された機能など

* `Number` の拡張

  ```js:ES2015.js
  Number.EPSILON;  // 1と Number として表現できる1より大きい最小の値の差
  Number.isInteger(Infinity); // false
  Number.isNaN('NaN'); // false
  ```

* `Math` の拡張

  ```js:ES2015.js
  Math.acosh(3); // ハイパボリックアークコサイン (双曲線逆余弦): 1.762747174039086 
  Math.hypot(3, 4); // 引数の二乗和の平方根: 5
  Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 32ビット乗算: 2
  ```
  
* `String` の拡張

  ```js:ES2015.js
  'abcde'.includes('cd'); // true
  'abc'.repeat(3); // 'abcabcabc'
  ```

* `Array` の拡張

  ```js:ES2015.js
  Array.from(document.querySelectorAll('*')); // 配列型や反復型オブジェクトから新しい Array インスタンスを生成
  Array.of(1, 2, 3); // 可変個の引数から新しい Array インスタンスを生成
  [0, 0, 0].fill(7, 1); // 開始位置〜終了位置を指定値で埋める: [0,7,7]
  [1, 2, 3].find(x => x == 3); // テスト関数を満たす、配列中の要素を返却: 3
  [1, 2, 3].findIndex(x => x == 2); // テスト関数を満たす、配列中の要素のインデックスを返却: 1
  [1, 2, 3, 4, 5].copyWithin(3, 0); // 配列内の要素の並びを指定位置にコピー: [1, 2, 3, 1, 2]
  ['a', 'b', 'c'].entries(); // 新しい Array Iterator オブジェクトを返却: [0, 'a'], [1, 'b'], [2, 'c']のイテレータ
  ['a', 'b', 'c'].keys(); // 新しい Array Iterator オブジェクトを返却（各 key を保持）: 0, 1, 2 のイテレータ
  ['a', 'b', 'c'].values(); // 新しい Array Iterator オブジェクトを返却（各 value を保持）: 'a', 'b', 'c'のイテレータ
  ```

* `Object` の拡張

  ```js:ES2015.js
  Object.assign(Point, { origin: new Point(0,0) }); // 一つ以上のソースオブジェクトから、直接所有（own）で列挙可能（enumerable）なすべてのプロパティ値をコピー
  ```

* イテレータ（`.next()`, `.values()`）
    * 現在の処理位置を把握しつつ、コレクション中の項目へ順にアクセスする方法を備えたオブジェクト
* ジェネレータ（`function*`, `yield`）
    * イテレータを返す特別な関数
* マップオブジェクト（`Map()`, `WeekMap()`）
    * key - value のコレクション
    * `Object` と似ているが少し違う
* セットオブジェクト（`Set()`, `WeekSet()`）
    * ユニークなデータのコレクション 
* `import` と `export` によるモジュール構文
    * 外部モジュールや関数、オブジェクト、プリミティブのインポート／エクスポートを行う
* シンボル（`Symbol()`）
    * ユニークで不変なデータ型
    * オブジェクトのプロパティ識別子などで使われる
    * Ruby: どれだけ作っても必ず同じオブジェクトになるもの
    * JS: 二度と同じオブジェクトを生成できないもの
* プロキシ（`Proxy()`）
    * オブジェクトが持つ機能を独自に定義できる 
* リフレクト（`Reflect`）
    * オブジェクトが本来持つ機能を返す
* 非同期処理（`Promise()`）
* 正規表現での Unicode 対応

* * *

## 参考

* ES2015 言語仕様 http://www.ecma-international.org/ecma-262/6.0/index.html
* ブラウザ対応 https://kangax.github.io/compat-table/es6/
* 新機能 https://github.com/lukehoban/es6features#readme
* 新機能 比較 http://es6-features.org/