JavaScriptでは[enum]という単語は予約語だけれど、一般的な[enum]、つまりは列挙型の仕組みは用意されていない。
[JavaScript enum]でGoogle検索をかけると、連想配列を使って擬似的に実現する方法が出てくるけれども、個人的には[定義時の見難さ]と[定義された値を後から書き換えることができてしまう]のが良くないと感じていた。
なので、この機会にその2点をカバーできるように仕組みを整えた。
コード: ENUM.js
class ENUM {
structure = this.define();
define () {
return Object;
}
apply (values) {
values == null ? values = {} : values;
let element = {};
Object.keys(new this.structure).forEach(key => Object.defineProperty(element, key, {
value: values[key],
writable: false
}));
return Object.freeze(element);
}
values () {
return Object.keys(this).map(key => this[key]);
}
static create (clazz) {
let instance = new clazz();
Object.defineProperty(instance, "structure", {
readable: false,
writable: false,
enumerable: false
});
return Object.freeze(instance);
}
};
コード: COLOR.js
let COLOR = ENUM.create(class extends ENUM {
RED = this.apply({code: "#FF0000"});
GREEN = this.apply({code: "#00FF00"});
BLUE = this.apply({code: "#0000FF"});
define () {
return class {
code;
};
}
});
[ENUM]というクラスは共通的に使い、新しく用意するenumの分だけ[COLOR.js]のような内容を用意してあげれば良い。
[define()]では、ENUMの値として使用するメンバーの名称定義してあげる感じになる。
COLORへlabelというメンバを増やしたCOLOR2を定義してみる。
コード: COLOR2.js
let COLOR = ENUM.create(class extends ENUM {
RED = this.apply({code: "#FF0000", label: "red"});
GREEN = this.apply({code: "#00FF00", label: "green"});
BLUE = this.apply({code: "#0000FF", label: "blue"});
define () {
return class {
code;
label;
};
}
});
メンバを省略した場合はdefine()の記述自体を省略できるようにした。
以下に使用例を載せる
例: sample.js
// 予めCOLORの定義は完了しているものとする
COLOR.ORANGE = "hoge"
COLOR.RED = null
COLOR.BLUE.code = "#FF0000";
COLOR.GREEN.code = "#FF0000";
COLOR.ORANGE = "hoge"
console.log("COLOR.RED == COLOR.RED: " + (COLOR.RED == COLOR.RED));
console.log("COLOR.RED == COLOR.GREEN: " + (COLOR.RED == COLOR.GREEN));
console.log("COLOR.RED == COLOR.BLUE: " + (COLOR.RED == COLOR.BLUE));
console.log("COLOR.RED.code: " + COLOR.RED.code);
console.log("COLOR.GREEN.code: " + COLOR.GREEN.code);
console.log("COLOR.BLUE.code: " + COLOR.BLUE.code);
console.log("COLOR.values(): " + COLOR.values());
switch(COLOR.RED) {
case COLOR.RED:
console.log("COLOR.RED IS CHOSEN.");
break;
case COLOR.GREEN:
console.log("COLOR.GREEN IS CHOSEN.");
break;
case COLOR.BLUE:
console.log("COLOR.BLUE IS CHOSEN.");
break;
default:
console.log("AN OTHER IS CHOSEN.");
}
結果: sample.js
COLOR.RED == COLOR.RED: true
COLOR.RED == COLOR.GREEN: false
COLOR.RED == COLOR.BLUE: false
COLOR.RED.code: #FF0000
COLOR.GREEN.code: #00FF00
COLOR.BLUE.code: #0000FF
COLOR.values(): [object Object],[object Object],[object Object]
COLOR.RED IS CHOSEN.
COLORへの要素の追加・変更・削除を許さない仕様なので、COLOR自体へ再代入されない限り思わぬところで値を書き換えられてしまう可能性がなくなる。
おまけでnode.js用
コード: ENUM.js
export default class {
structure = this.define();
define () {
return Object;
}
apply (values) {
values == null ? values = {} : values;
let element = {};
Object.keys(new this.structure).forEach(key => Object.defineProperty(element, key, {
value: values[key],
writable: false
}));
return Object.freeze(element);
}
values () {
return Object.keys(this);
}
static create (clazz) {
let instance = new clazz();
Object.defineProperty(instance, "structure", {
readable: false,
writable: false,
enumerable: false
});
return Object.freeze(instance);
}
};
コード: COLOR.js
import ENUM from './ENUM.js'
export default ENUM.create(class extends ENUM {
RED = this.apply({code: "#FF0000"});
GREEN = this.apply({code: "#00FF00"});
BLUE = this.apply({code: "#0000FF"});
define () {
return class {
code;
};
}
});
元々こっちで作っていたのに、ブラウザで検証するために上記のコードへ書き換えていたというオチ。