JavaScriptでJavaのようにフィールドやメソッドを持つ高機能なenumを作る

##Javaのenum
Javaのenumはとっても高機能で次のように複数のフィールドやメソッドを持つenumを作ることができる。

```Java.Color.java
enum Color {
	RED("赤", 255, 0, 0),
	YELLOW("黄色", 255, 255, 0),
	GRAY("灰色", 155, 155, 155);
	Color(String name, int r, int g, int b) {
		this.name = name;
		this.r = r;
		this.g = g;
		this.b = b;
	}
	public String name;
	public int r, g, b;
	public String toHexCode() {
		return String.format("#%02x%02x%02x", r, g, b);
	}
}
```

```Java
System.out.println(Color.YELLOW.toHexCode()); // => "#ffff00"
System.out.println(Color.GRAY.name); // => "灰色"
```

##JavaScriptで実装
これをJavaScriptで素直に実装するときっとこんな感じになるかと思う。

```JavaScript.color.js
var Color = (function() {
  var InnerType = function(name, r, g, b) {
    this.name = name;
    this.r = r;
    this.g = g;
    this.b = b;
  };
  InnerType.prototype.toHexCode = function() {
    return '#' + ('00' + this.r.toString(16)).slice(-2) + ('00' + this.g.toString(16)).slice(-2) + ('00' + this.b.toString(16)).slice(-2);
  };
  return {
    RED : new InnerType('赤', 255, 0, 0),
    YELLOW : new InnerType('黄色', 255, 255, 0),
    GRAY : new InnerType('灰色', 155, 155, 155)
  };
})();
```

```JavaScript
console.log(Color.YELLOW.toHexCode()); // => "#ffff00"
console.log(Color.GRAY.name); // => "灰色"
```
一見良さそうだけど、、、
JavaではこのenumをMapのkeyに使ったりが当たり前にできるが

```Java
Map<Color, Integer> colorNums = new HashMap<>();
colorNums.put(Color.RED, 10);
colorNums.put(Color.YELLOW, 20);
colorNums.put(Color.GRAY, 30);
System.out.println(colorNums.get(Color.RED)); // => "10"
System.out.println(colorNums.get(Color.YELLOW)); // => "20"
```

JavaScript版だと、

```JavaScript
var colorNum = {};
colorNum[Color.RED] = 10;
colorNum[Color.YELLOW] = 20;
colorNum[Color.GRAY] = 30;
console.log(colorNum[Color.RED]); // => "30" !!?
console.log(colorNum[Color.YELLOW]); // => "30" !!?
```

と、意図通りの挙動にならなかったり。
Color.RED, Color.YELLOW, Color.GRAYはオブジェクトなので、連想配列のキーになったときにオブジェクトのデフォルトのtoStringが呼び出されてしまい、3つとも"[object Object]"になってしまうのが原因。

##JavaScriptで実装(修正版)
なので、次のようにtoStringでそれぞれに固有の文字列を返すようにすると意図通りに動く。

```JavaScript.color_fix.js
var Color = (function() {
  const InnerType = function(name, r, g, b, toStringValue) {
    this.name = name;
    this.r = r;
    this.g = g;
    this.b = b;
    this.toString = function() {
      return toStringValue;
    };
  };
  InnerType.prototype.toHexCode = function() {
    return '#' + ('00' + this.r.toString(16)).slice(-2) + ('00' + this.g.toString(16)).slice(-2) + ('00' + this.b.toString(16)).slice(-2);
  };
  return {
    RED : new InnerType('赤', 255, 0, 0, 'RED'),
    YELLOW : new InnerType('黄色', 255, 255, 0, 'YELLOW'),
    GRAY : new InnerType('灰色', 155, 155, 155, 'GRAY')
  };
})();
```

```JavaScript
var colorNum = {};
colorNum[Color.RED] = 10;
colorNum[Color.YELLOW] = 20;
colorNum[Color.GRAY] = 30;
console.log(colorNum[Color.RED]); // => "10"
console.log(colorNum[Color.YELLOW]); // => "20"
```

めでたしめでたし。

##JavaScriptで実装(ES6版)
ただ、toString用の文字列を自分で指定するのが少し残念なので、ES6が使えるのならSymbol()を使って自動化すると良い感じ。

```JavaScript.color_fix_es6.js
const Color = (() => {
  const InnerType = function(name, r, g, b) {
    this.name = name;
    this.r = r;
    this.g = g;
    this.b = b;
    const sym = Symbol();
    this.toString = () => sym;
  };
  InnerType.prototype.toHexCode = function() {
    return '#' + ('00' + this.r.toString(16)).slice(-2) + ('00' + this.g.toString(16)).slice(-2) + ('00' + this.b.toString(16)).slice(-2);
  };
  return {
    RED : new InnerType('赤', 255, 0, 0),
    YELLOW : new InnerType('黄色', 255, 255, 0),
    GRAY : new InnerType('灰色', 155, 155, 155)
  };
})();
```

Javaのenum最強だけど、ほぼ同じことを実現できるJavaScriptの柔軟性よしよし。