JavaScript文法(12) ES6の新機能

# この記事の内容

* ES6の新機能について
* 従来のJavaScript（ES5）から、どのように強化されたか

# 目次

(1) [学習環境の構築と基本的な書き方](http://qiita.com/azk0305/items/a13d494178d83fc8f2bc)
(2) [変数とデータ型](http://qiita.com/azk0305/items/d78d744dff9656b22dd0)
(3) [演算子および配列](http://qiita.com/azk0305/items/748f4afadcfa5350a552)
(4) [制御構文と関数](http://qiita.com/azk0305/items/ed943040a8f7140d5a48)
(5) [関数の応用1](http://qiita.com/azk0305/items/f7e0653b20425fdb9f1b)
(6) [関数の応用2](http://qiita.com/azk0305/items/0fd54f9c66f726443dd6)
(7) [オブジェクトの基礎](http://qiita.com/azk0305/items/0d2018a1c6e547b7f65a)
(8) [JavaScriptのオブジェクト指向プログラミング1 概要](http://qiita.com/azk0305/items/1e1bd10522927eaddd6a)
(9) [JavaScriptのオブジェクト指向プログラミング2 オブジェクトの作り方と継承](http://qiita.com/azk0305/items/386b3a2ac41caf3101e1)
(10) [JavaScriptのオブジェクト指向プログラミング3 多態性](http://qiita.com/azk0305/items/1c713562f6f842dcf1dc)
(11) [JavaScriptのオブジェクト指向プログラミング4 カプセル化](http://qiita.com/azk0305/items/914f6e447b41d4ced25e)
(12) ES6の新機能について **<-- この記事の内容**

# ECMAScript バージョン6 (ES2015) について

一連の文法記事の最後として、通称ES6から搭載された新機能について、いくつかピックアップして観ていく。なお、ES6は、2015年6月に制定された規格である。詳細は各種ネット記事などを参照のこと。

# BabelでES6を試す

2016年現在、まだES6が各種ブラウザ等で確実に機能が実装されているとは言いがたい状況である。そこで、ES6の機能が確実に実行できる環境が「Babel」のサイトにある。

[http://babeljs.io/repl/](http://babeljs.io/repl/)

本来「Babel」は、ES6の機能を使って記述したJavaScriptの命令を、従来のJavaScriptのコード（ES5）に変換してくれるツールである。「altJS」のひとつと言っていいかと。上記のページは、ツールを導入する前にWeb上で手軽にBabelの機能を試せるページとなっている。今回の内容をBabelで変換かけてみると、どのような従来のコードに変換されるかを見てみても面白いかと思う。

# ES6の新機能まとめ

## const と let

従来のJavaScriptには、定数というものが存在していなかった。ES6で `const` というキーワードを使えば、定数を実現できる。

```js:bs12_01.js
const USER_MAX_VALUE = 100;
USER_MAX_VALUE = 200;          // 定数は上書きできない旨のエラーが出る
console.log(USER_MAX_VALUE);   // 2行目が無ければ 100 と表示される
```

次に`let`。以前の記事のとおり、ifやforなどのブロック内で宣言された変数は `var` をつけていてもブロックの外で参照できる。少々問題かなと思ってしまう。そこでES6ではブロックスコープを実現できる新しい要素として `let` というキーワードが追加された。 `var` ではなく `let` を使ってブロック内で変数を宣言すると、ブロックの外で該当の変数を参照できなくなる。

```js:bs12_02.js
if(true) {
  var a = 1;
  let b = 2;
}

console.log(a);    // 1 と表示される
console.log(b);    // bは定義されていない旨のエラーが出る
```

## 連想配列専用のオブジェクト Map

JavaScriptで連想配列を使うには、 `Object` 型のオブジェクトを作成して、プロパティに値を設定する形で使う旨、以前は説明した。ES6からは、`Map` という型のオブジェクトが追加された。わざわざ `Object` 型のオブジェクトを利用しなくても連想配列を利用できる。データの登録には `set` メソッド、指定したキーに紐づいた要素を取得するには `get` メソッドを利用する。

```js:bs12_03.js
// 新しいMapオブジェクトの生成
var map = new Map();

// mapにデータを新規登録
map.set("name", "田中 太郎");

// mapの指定要素を参照
console.log(map.get("name"));
```

## 集合体オブジェクト Set

`Map` と同時に `Set` がES6で追加された。`Set`はJavaのSetインターフェイスと同じく、集合体の形式でデータを管理するためのもの。同じデータを2個以上、重複してデータを管理することができない、という特徴がある。データの登録には `add` メソッドを使い、そのSetの中にデータがあるかどうかを調べるには `has` メソッドを使う。

```js:bs12_04.js
// 新しいSetオブジェクトの生成
var set = new Set();

// setにデータを新規登録
set.add("田中");
set.add("鈴木");
set.add("佐藤");
set.add("高橋");

if(set.has("佐藤")) {
  console.log("佐藤さんは、います。");    // この行は実行される
}
```

## for-of文

従来のJavaScriptには `for-in` 文があった。イテレータのように配列や連想配列から1件ずつ取り出してくれるものである。ただし、`for-in`文は、通常の配列の場合はインデックス（0, 1, 2, ... といった配列の添字）を、連想配列の場合はキーを1件ずつ取り出す形になる。

```js:bs12_05.js
var score = [70, 65, 55, 80, 100];

for(var s in score) {
  console.log(s);
}
```

```text:bs12_05.jsの実行結果（一例）
0
1
2
3
4
```

ES6で追加された `for-of` 文では、インデックスやキーではなく、値を1件ずつ取得してくれる。

```js:bs12_06.js
var score = [70, 65, 55, 80, 100];

for(var s of score) {
  console.log(s);
}
```

```text:bs12_06.jsの実行結果（一例）
70
65
55
80
100
```

なお、`for-of`文は、MapやSetでも利用できる。

# アロー関数

匿名関数では関数を定義する場合、下記のように書く必要があった。

```js
var f = function(name) {
  console.log("Hello, " + name + "!");
}

f("Taro");    // Hello, Taro! と表示される
```

ES6で追加されたアロー関数を利用すると、わざわざ `function` という単語を書く必要がなくなる。

```js:bs12_07.js
var f = (name) => {
  console.log("Hello, " + name + "!");
}

f("Taro");    // Hello, Taro! と表示される
```

`function(name) { ... }` と記述していた部分が `(name) => { ... }` というように短縮して記述できるようになった。

# 引数が未定義だった場合のデフォルト値の指定が可能に

オーバーロードのときの記事で、引数のチェックをして、引数が定義されていない（undefined）か否かで処理を分ける記述の仕方を観た。

```js
function hello(name) {
  // 引数が無かったら、nameを 名無し にする
  if(typeof name === "undefined") {
    name = "名無し";
  }
  
  console.log("こんにちは、" + name + "さん！");
}

hello();          // こんにちは、名無しさん！ と表示
hello("太郎");    // こんにちは、太郎さん！ と表示
```

ES6では、このようなチェックを自前で記述しなくても、引数が無かった場合のデフォルト値を指定することができるようになった。

```js:bs12_08.js
function hello(name = "名無し") {
  console.log("こんにちは、" + name + "さん！");
}

hello();          // こんにちは、名無しさん！ と表示
hello("太郎");    // こんにちは、太郎さん！ と表示
```

これで、引数があっても無くても、`hello` 関数が実行できる。

# class と extends が追加

以前、JavaScriptには `class` キーワードが無いと書いたが、あくまでも「ES5までの」JavaScriptは、であり、ES6から `class` キーワードが追加された。`extends` キーワードも追加されたため、よりオブジェクト指向プログラミングらしい記述ができるようになった。

```js:bs12_09.js
class Animal {
  // コンストラクタは constructor という名前のメソッドで固定
  constructor(name, voice) {
    // プロパティの定義
    this.name  = name;
    this.voice = voice;
  }
  
  // メソッドの定義
  bark() {
    console.log(this.name + "が鳴く：" + this.voice);
  }
}

// Dog クラス（Animalクラスを継承）
class Dog extends Animal {
  constructor(name, voice) {
    // 親クラスのコンストラクタを呼び出す
    super(name, voice);
  }
  
  // bark() メソッドをオーバーライド
  bark() {
    console.log(this.name + "が吠える：" + this.voice);
  }
}

// Cat クラス（Animalクラスを継承）
class Cat extends Animal {
  constructor(name, voice) {
    super(name, voice);
  }
}

// Dog と Cat の新しいオブジェクトを生成
var pochi = new Dog("ポチ", "わん！");
var tama  = new Cat("タマ", "にゃー！");

pochi.bark();       // ポチが吠える：わん！ と表示される
tama.bark();        // タマが鳴く：にゃー！ と表示される
```

※補足：スクラッチパッドで実行していて、

```text
SyntaxError: redeclaration of let Animal
@Scratchpad/1:2:7
```

このようなエラーが**文法的な記述ミスなど無いのに**表示された場合は、スクラッチパッドの上部のメニューから「実行メニュー」→「再読み込みして実行」をしてみると、エラーが回復する場合がある。

## 発展的内容：Promiseオブジェクト

非同期処理やコールバック関数の記事で、以下の様なコードを扱った。

```js
// 非同期処理の定義
function timeoutFunc(callback, delay) {
  setTimeout(function() {
    console.log("非同期処理　実行");
    callback();
  }, delay);
};

// プログラムの処理本体
console.log("プログラム　開始");
timeoutFunc(function() {
  console.log("非同期処理　終了");
}, 1000);
```

ES6からは、非同期処理を専門に扱うPromiseオブジェクトというものが追加された。コールバック関数とは別の方法で非同期処理を実装できる。以下は一例。

```js:bs12_10.js
// Promiseオブジェクトを使った非同期処理の定義
function timeoutFunc(delay) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      // resolve関数を介してconsole.logを実行
      resolve(console.log("非同期処理　実行"));
    }, delay);
  });
};

// プログラムの処理本体
console.log("プログラム　開始");

// then関数の引数にコールバックで実行したい処理を指定
timeoutFunc(1000)
  .then(function() {
    console.log("非同期処理　終了");
  });
```

Promiseオブジェクトの利点のひとつに、ネスト地獄に陥る可能性が低くなる、というものがある。たとえば、コールバック関数の中でコールバック関数を呼び出すような書き方をした場合、以下のような記述になってしまう。

```js
// 非同期処理の定義
function timeoutFunc(callback, delay) {
  setTimeout(function() {
    console.log("非同期処理　実行");
    callback();
  }, delay);
};

// プログラムの処理本体
console.log("プログラム　開始");
timeoutFunc(function() {
  timeoutFunc(function() {
    timeoutFunc(function() {
      timeoutFunc(function() {
        console.log("非同期処理　終了");
      }, 1000);
    }, 1000);
  }, 1000);
}, 1000);
```

幾重にもネストしなければならなくなる。これをPromiseオブジェクトを利用した形に書き換えると、以下のようになる。

```js:bs12_11.js
// Promiseオブジェクトを使った非同期処理の定義
function timeoutFunc(delay) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      // resolve関数を介してconsole.logを実行
      resolve(console.log("非同期処理　実行"));
    }, delay);
  });
};

// プログラムの処理本体
console.log("プログラム　開始");

// then関数の引数にコールバックで実行したい処理を指定
timeoutFunc(1000)
  .then(function() {
    timeoutFunc(1000);
  })
  .then(function() {
    timeoutFunc(2000);
  })
  .then(function() {
    timeoutFunc(3000)
      .then(function() {
        console.log("非同期処理　終了");
      });
  });
```

これが利点のひとつである。

なお、Promiseオブジェクトをまともに利用するためには、他にも知っておきたい要素がたくさんあるが、当記事ではここまでとさせていただく。興味ある人はググるなどして、更なる詳細を調べてほしい。

# その他の新機能

また、他にもES6からの新しい機能は沢山あるが、紹介しきれないのと、少し難しい話になるものもあるため、色々と興味がある人は、以下のようなサイトを参照してみると良いかと。（下のサイトは全て、英語圏のサイトである点、注意。）

ECMAScript 2015 Language Specification
[http://www.ecma-international.org/ecma-262/6.0/](http://www.ecma-international.org/ecma-262/6.0/)

ECMAScript 6: New Features
[http://es6-features.org](http://es6-features.org)

ECMAScript 6 compatibility table
[http://kangax.github.io/compat-table/es6/](http://kangax.github.io/compat-table/es6/)

* * *

JavaScriptの基礎編としての「文法」については以上となる。実践編としての「Webページ（HTML/CSS）におけるJavaScriptの使い方（イベント、DOMなど）」については、別の機会に。