LoginSignup
3
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-15

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);
    }
}
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)
  };
})();
console.log(Color.YELLOW.toHexCode()); // => "#ffff00"
console.log(Color.GRAY.name); // => "灰色"

一見良さそうだけど、、、
JavaではこのenumをMapのkeyに使ったりが当たり前にできるが

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版だと、

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')
  };
})();
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の柔軟性よしよし。

3
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2