ES6 Symbol を使ってオレオレメソッドを拡張する

[ECMAScript6にシンボルができた理由](http://qiita.com/naruto/items/312adeb6145eb6221be7) で紹介されている [Symbol](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol) の使い方を見て、fmfm するためのエントリです。

# 似て非なる 2つの NativeObject 拡張ライブラリを同時に読み込むとどうなるか

**例**

Array に `JSON.stringify(Array)` 相当の機能を拡張するライブラリが2つ(AwesomeArray.js, ExArray.js)あります。これらを読み込むと Array#toJSON が拡張されます。
AwesomeArray.js と ExArray.js には、カンマの後ろにスペースがある/ない という違いがあります。

## ES5 式

ES5 で書かれた AwesomeArray.js と ExArray.js です。

ES5 までは、AwesomeArray.js を読み込んだ後に ExArray.js を読み込むと、AwesomeArray.js で拡張した Array#toJSON が ExArray.js のそれに上書きされてしまいます。
AwesomeArray.js と ExArray.js が混在してしまう状況では、最終的にどちらが機能するかは、読み込み順に依存することになりカオスでした。

```js
// AwesomeArray.js
(function(global) {
  Array.prototype.toJSON = function() {
    return "[" + this.join(",") + "]"; // without space
  };
})(this);
```

```js
// ExArray.js
(function(global) {
  Array.prototype.toJSON = function() {
    return "[" + this.join(", ") + "]"; // with space
  };
})(this);
```

```js
var array = [1,2,3];
console.log(array.toJSON()); // -> [1, 2, 3] (ExArray.js の Array#toJSON が機能している)
```

## ES6 式

ES6 からは、Symbol を使う事でこれら2つのライブラリが共存可能になり、呼び分ける事も可能になります。

```js
// AwesomeArray.js
(function(global) {
  // シンボルを作成します。文字列 "AwesomeArray Array#toJSON" はデバッグ時の識別用です
  var toJSON = Symbol("AwesomeArray Array#toJSON");

  // JSON.stringify(Array) 相当のメソッドを追加します。
  // Symbol で追加するため for in などの enum ではメソッド名は列挙されません
  Array.prototype[toJSON] = function() {
    return "[" + this.join(",") + "]"; // without space
  };

  // Symbol を export しライブラリの外から呼び出し可能にします
  global.AwesomeArray = { "toJSON": toJSON }; // export Symbol
})(this);
```

```js
// ExArray.js
(function(global) {
  // シンボルを作成します。文字列 "ExArray Array#toJSON" はデバッグ時の識別用です
  var toJSON = Symbol("ExArray Array#toJSON");

  // JSON.stringify(Array) 相当のメソッドを追加します。
  // Symbol で追加するため for in などの enum ではメソッド名は列挙されません
  Array.prototype[toJSON] = function() {
    return "[" + this.join(", ") + "]"; // with space
  };

  // Symbol を export しライブラリの外から呼び出し可能にします
  global.ExArray = { "toJSON": toJSON }; // export Symbol
})(this);
```

### Test

AwesomeArray.js と ExArray.js を読み込み、
AwesomeArray.js で追加した Array#toJSON と ExArray.js で追加した Array#toJSON が共存できているかテストします。

```js
require("AwesomeArray.js");
require("ExArray.js");

var array = [1,2,3];
var toJSON = AwesomeArray.toJSON;

console.log( array[AwesomeArray.toJSON]() ); // -> "[1,2,3]"
console.log( array[ExArray.toJSON]() );      // -> "[1, 2, 3]"
console.log( array[toJSON]() );              // -> "[1,2,3]"
```


# Object.getOwnPropertySymbols

`for ... in` や `Object.getOwnPropertyNames` で Symbol が見えない事を確認します

```js
console.log( Object.getOwnPropertyNames(Array.prototype) );
// -> [ "length", "constructor", "toString", "toLocaleString",
//      "join", "pop", "push", "concat", "reverse", "shift",
//      "unshift", "slice", "splice", "sort", "filter", "forEach",
//      "some", "every", "map", "indexOf", "lastIndexOf", "reduce",
//      "reduceRight", "entries", "keys", "copyWithin", "find",
//      "findIndex", "fill", "includes" ]
```

[`Object.getOwnPropertySymbols`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols) で、Symbol を可視化できます。

```js
console.log( Object.getOwnPropertySymbols(Array.prototype) );
// -> [ Symbol(Symbol.unscopables),
//      Symbol(Symbol.iterator),
//      Symbol(AwesomeArray Array#toJSON),   <- 追加されている
//      Symbol(ExArray Array#toJSON) ]       <- 追加されている
```