ES6(ES2015)チートシート

素のままのJavaScript(ES5)に直接触れる機会もだんだんと減ってきたので、うろ覚えだったES6の機能をまとめてみました。
なお文中のコードはBabel環境で書きながら確認したため、現行ブラウザでの直接の対応状況に関しては未調査です。


概要
------------------------------------------------------------

ECMAScriptS6 = ES2015 = JavaScriptの新しいバージョン

- ES5(現行のjs)の次のバージョンとして策定された
- さらに新しいES7も作られている

現状は多くのブラウザが(部分的にしか)対応していないので、ES6で書いたコードを動かすためにはES5への変換(トランスパイル)が必要になる。
トランスパイル用のツールとしてはBabelが代表的な実装になる。

具体的な利用方法としては:


- ES6をサポートしているブラウザを使う
    - 現状、サポートは部分的かつ挙動に差異がある

- babelのCLIを使って直接トランスパイルする

- webpack, browserify, sprocketsなどがバンドルを作成する過程にBabelを組み込む

- Bebel/browser.jsを使って表示のたびにトランスパイルする(デバッグ用)


など。

導入方法は以前React-Redux環境構築の際に調査済なので、今回は省略する。
([React-Redux on Rails(w/webpack/yarn)環境を作ったメモ](http://qiita.com/morrr/items/461008894d24bd7691ac))


構文の追加/変更
------------------------------------------------------------

### モジュール(import/export)

CommonJSなどサードパーティレベルで実現していたファイル分割/依存関係解決の機能が標準機能となった。
あるモジュール(ファイル)からexportしたクラス・変数・定数・関数を、他のモジュールがimportするかたちで使う。

import/exportの指定方法は復数あるので詳細は[MDNなど](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import)を参照のこと。


```js:exportする例

// 変数・定数・関数を、名前を指定して直接出力する
export let someVariable = ...
export const SOME_CONST = ...
export const someFunc = () => {
  ...
}

// デフォルト(ファイル=モジュールにひとつ)として出力する
export default class SomeClass{
  ...
}
```

```js:importする例

// モジュール内の全てをSomeModuleにバインドする
import * as SomeModule from 'path/to/module'

// デフォルトを名前空間SomeModuleにバインドする
import SomeModule from 'path/to/module'

// モジュールをバインドせずにロードだけする
import 'path/to/module'

// モジュール内のfooとbarだけロードする
import {foo, bar} from 'path/to/module'
```


### 変数と定数(let/const)

`var`の代替として、`let`と`const`を変数の宣言時に使える。

constで宣言すると定数となり、変更できない。
(Babel環境の場合はトランスパイル時に静的なエラーとなる

letの機能はvarと同じ。
constの対比として、変更できることを明示する意味合いで使う。

```js
let foo = 'foo'
const bar = 'bar'

// OK
foo = 'hoge'

// Error
bar = 'piyo'
```


### クラス(class/extends/super/static)

クラス定義を構文レベルでサポートする。
(シンタックスシュガーにすぎないので、内部的には従来のプロトタイプベース構造から変わっていないとのこと)

```js
class Foo extends SomeSuperClass{

  // コンストラクタ
  constructor(bar, baz){

    // 親クラスのメソッドが呼べる
    super()

    // インスタンス変数はコンストラクタ内で定義する(これまでと同じ)
    this._bar = baz
    this._baz = baz
  }

  // インスタンスメソッド
  bar(){
    return this._bar
  }

  // クラスメソッド
  static foo(){
    return 'FOO'
  }
}

let foo = new Foo('BAR', 'BAZ')
console.error(foo.bar())
console.error(Foo.foo())
```

### オブジェクト定義の簡略化

オブジェクト定義の構文が強化された。

```js
let magicKey = 'foo'

let obj = {
  // プロトタイプを指定する
  __proto__: theProtoObj,

  // キー名を省略できる(foo: foo, と同義)
  foo,

  // メソッドを簡単に書ける
  someMethod(){
    // クラスと同様にsuper()を呼べる
    super()
  },

  // 動的なキーを使う(obj.foo)
  [ magicKey ]: 'magic value'
}
```


### アロー関数(=>)

関数定義の省略記法として`()=>`が使える。
アロー関数中の`this`は、その__外側のスコープのthisに等しい__(いわゆる`self`を定義しなくていい)

```js
// 基本形
let allowFunc = () => {
  // ...
}

// 外側のthisを参照できる
var someObj = {
  _someParam: '...',

  getSomeParam(){
    return () => {
      this._someParam  // ここでのthisはsomeObj
    }
  },
}

// 単行の場合は省略可能
let simpleFunc = () => otherFunc()

// 引数がひとつの場合は括弧も省略可能
let funcWithArg = arg => otherFunc(arg)

```

### テンプレート文字列(`${}`)

文字列中に変数を埋め込んで、その内容を展開できる。
テンプレート文字列は`\``で囲む。

(ちなみにmarkdownでよく使うこの文字は、[グレイヴ・アクセント](https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AC%E3%82%A4%E3%83%B4%E3%83%BB%E3%82%A2%E3%82%AF%E3%82%BB%E3%83%B3%E3%83%88)というらしい)


```js
const foo = 'foo'
const bar = 'bar'
const baz = () => {
    return 'baz'
}

// 変数を展開する
const foobar = `${foo}, ${bar}`

// 式の評価もできる
const some = `${baz()}`
```

### 2進/8進リテラル(0b/0o)

2進数、8進数をリテラルとして表現できるようになった。

```js
let bin = 0b1001    // 2進
let oct = 0o11      // 8進
let dec = 9         // 10進
let hex = 0x09      // 16進
```

### デフォルト / 可変長引数(=/...)

引数にデフォルト値を設定できるようになった。
また可変長にも対応した。

```js
// デフォルトをとる
function add(x, y=0){
  return x + y
}
add(3)
add(4, 5)


// 可変長引数をとる
function sum(...args){
  return args.reduce(function(count, val){
    return count + val
  }, 0)
}
sum(1, 2, 3)
```


### コレクションの繰り返し(for-of)

繰り返しの制御構文としてfor-of文が追加された。

既存のfor-in との違いは、オブジェクト以外のコレクション(Array, Map, Set, Iterator, String)にも対応していること。
また後述のイテレータを回すこともできる。


```js
// 配列を回す(for-in): for-inなので"インデックス"が"文字列"で返る('0', '1', '2')
for(let n in [10, 20, 30])
  console.log(n)

// 配列を回す(for-of): 整数型の値10, 20, 30が返る
for(let n of [10, 20, 30])
  console.log(n)

// 文字列も回せる
for(let n of 'abc')
  console.log(n)
```

### 分割代入

英語ではDestructuring(非構造化)とのこと。

配列やオブジェクトの内容を、復数の変数に(分割して)代入する。

```js
// 配列を分割する
const [foo, bar] = ['foo', 'bar']
console.log(foo)
console.log(bar)

// オブジェクトを分割する(大文字がオブジェクトのキー、小文字が代入される変数)
const {FOO: hoge, BAR: piyo} = {'FOO' : 'foo', 'BAR' : 'bar' }
console.log(hoge)
console.log(piyo)
```

### 配列展開

`...`で配列を展開し、引数や配列の初期化処理内で利用できる。


```js
let list = [3, 4, 5]

// 展開して結合する
list = [1, 2, ...list, 6, 7]
console.log(list)


// 展開して可変長引数に渡す
print(...list)

function print(...args){
    console.log(args)
}
```




型とオブジェクトの追加/変更
------------------------------------------------------------

### 基本型の継承(Array, Date, Element)

ES5では継承できなかった(推奨されていなかった) Array, Date, Elementが継承可能になった。

```js
class CustomArray extends Array{
  // ...
}
```


### シンボル型(Symbol)

シンボルは、文字列に似た一意なオブジェクト。

- __プリミティブ型__である
- それ自身とのみ等価(==)、等値(===)となる


```js
let secretKey = Symbol('foo')
let anotherKey = Symbol('foo')

// 同じ文字列で初期化しても、シンボルは別オブジェクトとして認識される
console.log(secretKey == anotherKey)    // false
console.log(secretKey === anotherKey)   // false

// 文字と比較しても同様
console.log(secretKey == 'foo')         // false
console.log(secretKey === 'foo')        // false

// 自身とだけ合致する
console.log(secretKey == secretKey)     // true
console.log(secretKey === secretKey)    // true
```

rubyの`:symbol`のようなリテラル表記は特にない様子だった。


### コレクション型(Map/Set/WeakMap/WeakSet)

- Set = 重複のない配列

- Map = "連想配列"に近いもの
    - 既存のObject(`{ }`)より格納の自由度が高い
    - かつ、操作用のメソッドが色々ある

- WeakSet / WeakMap = 弱参照のSet/Map
    - 参照(オブジェクト)のみ保持できる
    - 格納しても、オブジェクトを保持しない(弱参照) = GCされうる



```js:Set
let set = new Set()
set.add('foo')
set.add('foo')              // 値の重複はエラーとはならないが、数にはカウントされない
set.add('bar').add('bar')   // メソッドチェーンできる

console.log(set.size)       // 2
console.log(set.has('foo')) // true
```

```js:Map
let map = new Map()

let strKey = 'key'
let listKey = [0, 1, 2]

map.set(strKey, 'string value')
map.set(listKey, 'list value')  // 配列オブジェクトをキーにすることができる

console.log(map.get(strKey))    // 'string value'
console.log(map.get(listKey))   // 'list value'
```


### プロミスパターン(Promise)

Promiseパターン実装のためのオブジェクト。
非同期処理を先に実行し、その後からでも結果のコールバックが設定できる。
なのでこれを使うと、

- 処理の順序とコードの記述順序が同じになる
- 非同期処理が連続する場合に、コード上の深いネストを避けられる


```js
const promise = new Promise((resolve, reject) => {
  // ...何か非同期的な処理を行う

  // 成功したら呼ぶ
  resolve()
  // 失敗したら呼ぶ
  reject()
})


promise.then(() => {
  console.log('success');
})
    
promise.catch(err => {
  console.error('failed: ' + err)
})
```

### プロキシパターン(Proxy)

Proxyパターン実装のためのオブジェクト。
もとのオブジェクトをラップしてふるまいを追加/変更する。

```js
// Proxyにつつまれるオブジェクト
var data = {};

// Proxyの処理内容
var handler = {
  get: function(obj, name){

    // ...

    return obj[name]
  },

  set: function(obj, name, value){

    // ...

    obj[name] = value
    return true
  },
};

// Proxyオブジェクトを作って操作
var proxy = new Proxy(data, handler)
proxy.foo = 'FOO'
proxy.bar = 'BAR'
console.log(proxy.foo)
console.log(proxy.bar)
```

ハンドルできるイベントは`get`, `set`, `construct`, `deleteProperty`など色々。

### イテレータ(Symbol.iterator)

任意のオブジェクトの`Symbol.iterator`プロパティにイテレータを定義することで、for-ofによってループを回すことができる。

```js
let list = {}

// イテレータを定義する
list[Symbol.iterator] = function(){
  let i = 0
  let iter = {
    next: () => {
      return { done: false, value: i++}
    }
  }

  return iter
}

// forで取得する
let iter = list[Symbol.iterator]()
for(let i=0; i < 10; i++){
    let result = iter.next()
    if(result.done) break
    console.log(result.value)
}

// for-ofで取得
for(let n of list){
  if(n >= 10) break
  console.log(n)
}
```

### ジェネレータ

`function*`でPython方式のジェネレータを定義できる。

なおBabelでジェネレータ構文を使う場合は`babel-polyfill`パッケージが必要だった。

```js
require('babel-polyfill')

let list = {}

// ジェネレータを定義する
const gen = function*(){
  for(let i=0;;)
    yield i++
}

// for-ofで生成する
for(let n of gen()){
  if(n >= 10) break
  console.log(n)
}
```


参考
------------------------------------------------------------
- [Wikipedia - ECMAScript](https://ja.wikipedia.org/wiki/ECMAScript)
- [Babel](https://babeljs.io/)
    - [Learn ES2015](https://babeljs.io/learn-es2015/)

- MDN
    - [Mozilla における ECMAScript 6 のサポート](https://developer.mozilla.org/ja/docs/Web/JavaScript/ECMAScript_6_support_in_Mozilla)
    - [for...of](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...of)
    - [分割代入](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
    - [Proxy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
    - [export](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export)
    - [import](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import)

- Stack Overflow
    - [What is the difference between ( for… in ) and ( for… of ) in javascript?](https://stackoverflow.com/questions/29285897/what-is-the-difference-between-for-in-and-for-of-in-javascript)
    - [ReferenceError: regeneratorRuntime is not defined (but working inside a scope)](https://stackoverflow.com/questions/36619383/referenceerror-regeneratorruntime-is-not-defined-but-working-inside-a-scope)

- [Qiita - Promiseと仲良くなって気持ち良く非同期処理を書こう](http://qiita.com/progre/items/03626b7f4655007d8cb2)