babelで扱えるEcmaScript6の新オブジェクト 〜 JSおくのほそ道 #033

こんにちは、ほそ道です。

今回はES6のシンタックスではなくオブジェクトについて、
今のところ使い出あるかなー、知っといたほうがいいかなーというものを備忘録的にまとめてみました。
一つ一つはもっと掘り下げ甲斐のあるものもあるので、細かい挙動を追う記事を別途書くかもしれません。

[目次はこちら](http://qiita.com/setzz/items/ad9a9c99a4fda041a563)

# 実行方法
## 実行環境準備

簡単に実行環境から紹介します。
今回はscript.jsというファイルに書いたES6コードを実行していきましょう。
まずはプロジェクトディレクトリを作って、コマンドを実行します。

```bash:コンソール
npm init  
npm i -D babel  
npm i -D babel-runtime
touch script.js
```

## script.jsの実行

例としてscript.jsを下記のようにしてみます。

```js:script.js
console.log(Symbol());
```

script.jsに処理を記述したら下記のコマンドで動かします。

```bash:コンソール
babel --optional runtime script.js | iojs
```

※iojsはnodeでも良いですが、どこかしら実行結果が変わる可能性はあります(バージョンも絡みますのでその辺はよしなにしてくださいませ)
※オブジェクトが無いと言われてしまう場合、JSソースの頭に`require('babel/polyfill');`をつけてみてください。

# Symbol

## Symbol型
- ES6で登場した新しい型。
- 各`Symbol()`はユニークとなる。
- 引数を与えることができるがSymbolの意味を表すもので、デバッグなどで使われる。処理上で意味を持たせることはできない。

```js:Symbolその1
const s = Symbol();

console.log(typeof s);  // symbol

console.log(Symbol('desc') === Symbol('desc')); // false

console.log(Symbol('desc')); // Symbol(desc)
```

- Symbolはオブジェクトのキーにできる
- Symbolを持っていないスコープはそのプロパティにはアクセスできないので、値をスコープ内で隠匿できる。
- ただし`Object.getOwnPropertySymbols`や`Reflect.ownKeys`を使うとプロパティに使用しているシンボルを取得できてしまう。

```js:Symbolその2
const o = {val: 100};

(() => {
  const s = Symbol('hundred');
  o[s] = 100;
  console.log(o[s]);
})();

// ここでは基本的にsは参照できない
for (let p in o) {
  console.log(p);  // valしか出てこない
}

// 抜け道的にSymbol取得
console.log(Object.getOwnPropertySymbols(o)[0]);  // Symbol(hundred)
console.log(Reflect.ownKeys(o)); // [ 'val', Symbol(hundred) ]
```

- Symbolはユニークなのでオブジェクトのプロパティとした時、当然プロパティもユニークなものとなる。

```js:Symbolその3-1
const o = {},
  a = Symbol('xxx'),
  b = Symbol('xxx');

o[a] = 100;
o[b] = 200;

console.log(o[b]);  // 200
console.log(o[a]);  // 100(上書きされない)
```

- `for`メソッドを使うことでもSymbolを生成できる。その場合、引数はシンボルにアクセスするためのキーとしての意味合いを持つ。
- キーは`keyFor`メソッドで取り出すこともできる。

```js:Symbolその3-2
const o = {},
  a = Symbol.for('xxx'),
  b = Symbol.for('xxx');

console.log(a === b);  // true
console.log(a);        // Symbol(xxx)

o[a] = 100;
o[b] = 200;

console.log(o[b]);  // 200
console.log(o[a]);  // 200(上書きされる)

console.log(Symbol.keyFor(a)); // xxx
```

## Symbol.iterator
- Symbol.iteratorは組み込みのシンボルで、ES6で定義されたIterableとIteratorというインターフェースを実現する
- IterableはIteratorを返す`[Symbol.iterator]`メソッドを実装するインターフェースで、`for..of`が使える
- Iteratorは`next`メソッドを実装するインターフェース

```js:Iterator
const iterableImpl = {
  vals: {p: 1, q: 2, r: 3},
  [Symbol.iterator]() {
    const self = this,
      keys = Object.keys(self.vals);
    return {
      next: () => {
        const nextKey = keys.shift();
        return {value: self.vals[nextKey], done: !nextKey};
      }
    }
  }
};

const itratorImpl = iterableImpl[Symbol.iterator]();
console.log(itratorImpl.next());  // { value: 1, done: false }
console.log(itratorImpl.next());  // { value: 2, done: false }
console.log(itratorImpl.next());  // { value: 3, done: false }
console.log(itratorImpl.next());  // { value: undefined, done: true }

for (let e of iterableImpl) {
  console.log(e);  // "1" "2" "3"
}
``` 

# コレクション
## WeakMap
- key-valueの形で要素を保持する
- キーはオブジェクトでなければならない
- 弱い参照を持ち、キーが他のどこからも参照されない場合、キーはGCの対象になる
- 反復処理はできない

```js:WeakMap
class Hoge {}
const a = new Hoge, b = new Hoge, c = new Hoge;

const wm = new WeakMap([[a, 100], [b, 200]]);
wm.set(b, 300);

console.log(wm.get(a));  // 100
console.log(wm.get(b));  // 300
console.log(wm.get(c));  // undefined

wm.set({}, 300);  // {}はGCの対象になる
```

## Map

- WeakMapとの比較
  > - オブジェクト以外の値(Number, Bool, String)もキーになる
  > - `forEach`, `keys`, `for..of` などの反復処理を実現する機能を持つ
  > - 強い参照を持ち、キーとなったオブジェクトはGC対象にならない(メモリを食う)

- 一般的なオブジェクトとの比較
  > - 数値やオブジェクトをキーにすることが容易
  > - サイズを得ることが容易

```js:Map
class Hoge {}
const a = new Hoge;

const m = new Map([[1, "x"], [2, "y"]]);
m.set(3, "z");
m.set(a, "hoge");

console.log(m.get(2));  // y
console.log(m.get(3));  // z
console.log(m.get(a));  // hoge

m.forEach((v, k) => {
  console.log(`${k} -> ${v}`); //"1 -> x" "2 -> y" "3 -> z" "[object Object] -> hoge"
});

for (let e of m) {
  console.log(e); // "[ 1, 'x' ]" "[ 2, 'y' ]" "[ 3, 'z' ]" "[ {}, 'hoge' ]"
}

m.set({}, "fuga");  // WeakSetと異なり、{}はGCに回収されない

console.log(m.size);    // 5
```

## WeakSet

- Mapと違って一意なオブジェクトを保持する。
- 格納できるのはオブジェクトのみ。
- 弱い参照を持ち、要素が他のどこからも参照されない場合、要素はGCの対象になる
- 要素の取り出しができないので状態管理あたりが用途になるかと思います。例えば一度使ったものはココに入れ、使用済みかどうかのチェックに使うなど。

```js:WeakSet
const a = {v: 1}, b = {v: 2}, c = {v: 3};

const ws = new WeakSet([a, b]);
ws.add(c);

console.log(ws.has(a));  // true
console.log(ws.has(b));  // true

ws.delete(a);
console.log(ws.has(a));  // false

ws.add({});  // {}はGCの対象になる
```

## Set

- WeakSet-Setの関係がWeakMap-Mapの関係とほぼ同じ

```js:Set
const s = new Set([{v: 1}, true, 1, 'moji']);
s.add({v: 2});

s.forEach(v => {
  console.log(v); // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
});

for (let e of s) {
  console.log(e); // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
}

for (let e of s.values()) {
  console.log(e);   // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
}

console.log(s.delete(1));   // true
console.log(s.delete(2));  // false

console.log(s.size); // 4
```

# Promise

- 処理結果が期待値を返すかどうか不明な処理の呼び出しにおいて、成功時/失敗時それぞれのコールバックを定義できる。
- サーバ通信などの非同期処理で用いるイメージ。

```js:Promise
const getEven = new Promise((resolve, reject) => {
  const even = Math.floor(Math.random() * 10);
  if (even % 2 === 0) {
    resolve(even);
  } else {
    reject(even);
  }
});

Promise.all(getEven.then(result =>{
  console.log(`getting even successful: ${result}`);
}).catch(reject => {
  console.log(`value error: ${reject}`);
}));
```

# Object.assign
これだけ既存オブジェクトのメソッドになっちゃいますが使い出ありそうだったので。

- 第一引数のオブジェクトに第二引数のオブジェクトが持つメンバを割り当てる

```js:Object.assign
class Man {
  constructor(name, age) {
    Object.assign(this, {
      name: name,
      age: age,
      greet: () => `Hello I'm ${this.name}, I'm ${this.age} years old`
    })
  }
}

const hide = new Man('Hide', 30);
console.log(hide.greet());  // Hello I'm Hide, I'm 30 years old
```

今回は以上です。