مقدمة - ما هي الصيغة المفضلة لتعريف التعدادات في JavaScript؟



مقدمة عن javascript (20)

ما هي الصيغة المفضلة لتعريف التعدادات في JavaScript؟ شيء مثل:

my.namespace.ColorEnum = {
    RED : 0,
    GREEN : 1,
    BLUE : 2
}

// later on

if(currentColor == my.namespace.ColorEnum.RED) {
   // whatever
}

أم أن هناك تعبيرًا أكثر تفضيلاً؟

https://code.i-harness.com


أجوبتك معقدة للغاية

var buildSet = function(array) {
  var set = {};
  for (var i in array) {
    var item = array[i];
    set[item] = item;
  }
  return set;
}

var myEnum = buildSet(['RED','GREEN','BLUE']);
// myEnum.RED == 'RED' ...etc

إذا كنت تستخدم Backbone ، فيمكنك الحصول على وظيفة التعداد الكامل (البحث عن طريق id ، والاسم ، والأعضاء المخصصين) مجانًا باستخدام Backbone.Collection .

// enum instance members, optional
var Color = Backbone.Model.extend({
    print : function() {
        console.log("I am " + this.get("name"))
    }
});

// enum creation
var Colors = new Backbone.Collection([
    { id : 1, name : "Red", rgb : 0xFF0000},
    { id : 2, name : "Green" , rgb : 0x00FF00},
    { id : 3, name : "Blue" , rgb : 0x0000FF}
], {
    model : Color
});

// Expose members through public fields.
Colors.each(function(color) {
    Colors[color.get("name")] = color;
});

// using
Colors.Red.print()

إليك ما نريده جميعًا:

function Enum(constantsList) {
    for (var i in constantsList) {
        this[constantsList[i]] = i;
    }
}

الآن يمكنك إنشاء التعدادات الخاصة بك:

var YesNo = new Enum(['NO', 'YES']);
var Color = new Enum(['RED', 'GREEN', 'BLUE']);

من خلال القيام بذلك ، يمكن إدخال الثوابت بالطريقة المعتادة (YesNo.YES ، Color.GREEN) ويحصلون على قيمة int متسلسلة (NO = 0 ، YES = 1 ؛ RED = 0 ، GREEN = 1 ، BLUE = 2).

يمكنك أيضًا إضافة أساليب ، باستخدام Enum.prototype:

Enum.prototype.values = function() {
    return this.allValues;
    /* for the above to work, you'd need to do
            this.allValues = constantsList at the constructor */
};


تحرير - تحسين صغير - الآن مع varargs: (لسوء الحظ أنه لا يعمل بشكل صحيح على IE: S ... يجب أن يلتزم بالإصدار السابق ثم)

function Enum() {
    for (var i in arguments) {
        this[arguments[i]] = i;
    }
}

var YesNo = new Enum('NO', 'YES');
var Color = new Enum('RED', 'GREEN', 'BLUE');

إنشاء كائن حرفي للكائن:

const Modes = {
  DRAGGING: 'drag',
  SCALING:  'scale',
  CLICKED:  'click'
};

خلاصة القول: لا يمكنك ذلك.

يمكنك تزييفها ، لكنك لن تحصل على أمان من النوع. عادة ما يتم ذلك عن طريق إنشاء قاموس بسيط لقيم السلسلة التي تم تعيينها إلى قيم صحيحة. فمثلا:

var DaysEnum = {"monday":1, "tuesday":2, "wednesday":3, ...}

Document.Write("Enumerant: " + DaysEnum.tuesday);

المشكلة مع هذا النهج؟ يمكنك إعادة تعريف العداد الخاص بك عن طريق الخطأ ، أو عن طريق الخطأ وجود قيم عددة مكررة. فمثلا:

DaysEnum.monday = 4; // whoops, monday is now thursday, too

تصحيح

ماذا عن كائن Art.Creeze في Artur Czajka؟ ألن يعمل هذا لمنعك من الإعداد يومًا إلى يوم الخميس؟ - فراي رباعية

على الاطلاق ، كان Object.freeze حل المشكلة التي اشتكى عنها تمامًا. أود أن أذكر الجميع أنه عندما كتبت أعلاه ، لم يكن Object.freeze موجودًا حقًا.

الآن .... الآن أنه يفتح بعض الاحتمالات المثيرة للغاية .

تحرير 2
إليك مكتبة جيدة جدًا لإنشاء التعدادات.

http://www.2ality.com/2011/10/enums.html

على الرغم من أنه من المحتمل أنه لا يلائم كل الاستخدامات الصالحة للتعدادات ، إلا أنه يستغرق وقتًا طويلاً جدًا.


طريقة سريعة وبسيطة هي:

var Colors = function(){
return {
    'WHITE':0,
    'BLACK':1,
    'RED':2,
    'GREEN':3
    }
}();

console.log(Colors.WHITE)  //this prints out "0"

في معظم المتصفحات الحديثة ، هناك نوع بيانات symbol البدائي الذي يمكن استخدامه لإنشاء التعداد. سيضمن ضمان سلامة التعداد ، حيث أن كل قيمة رمزية مضمونة بجافا سكريبت لتكون فريدة ، أي Symbol() != Symbol() . فمثلا:

const COLOR = Object.freeze({RED: Symbol(), BLUE: Symbol()});

لتبسيط التصحيح ، يمكنك إضافة وصف لقيم التعداد:

const COLOR = Object.freeze({RED: Symbol("RED"), BLUE: Symbol("BLUE")});

عرض Plunker

على GitHub يمكنك العثور على غلاف يبسط الكود المطلوب لتهيئة التعداد:

const color = new Enum("RED", "BLUE")

color.RED.toString() // Symbol(RED)
color.getName(color.RED) // RED
color.size // 2
color.values() // Symbol(RED), Symbol(BLUE)
color.toString() // RED,BLUE

لا يدعم IE8 أسلوب التجميد ().
المصدر: http://kangax.github.io/compat-table/es5/ ، انقر على "إظهار المتصفحات القديمة؟" في الأعلى ، وتحقق من IE8 وتجمد تقاطع الصفوف.

في مشروع الألعاب الحالي ، استخدمت أدناه ، حيث لا يزال عدد قليل من العملاء يستخدمون IE8:

var CONST_WILD_TYPES = {
    REGULAR: 'REGULAR',
    EXPANDING: 'EXPANDING',
    STICKY: 'STICKY',
    SHIFTING: 'SHIFTING'
};

يمكننا أيضًا القيام بما يلي:

var CONST_WILD_TYPES = {
    REGULAR: 'RE',
    EXPANDING: 'EX',
    STICKY: 'ST',
    SHIFTING: 'SH'
};

أو حتى هذا:

var CONST_WILD_TYPES = {
    REGULAR: '1',
    EXPANDING: '2',
    STICKY: '3',
    SHIFTING: '4'
};

أما الطريقة الأخيرة ، فيبدو أنها الأكثر كفاءة بالنسبة إلى السلسلة ، فهي تقلل من إجمالي النطاق الترددي إذا كان لديك خادم وعميل يتبادلان هذه البيانات.
بالطبع ، من واجبك الآن التأكد من عدم وجود تعارض في البيانات (RE، EX ، إلخ. يجب أن تكون فريدة ، يجب أن تكون 1 ، 2 ، إلخ. فريدة من نوعها). لاحظ أنك تحتاج إلى الحفاظ على هذه إلى الأبد للتوافق مع الإصدارات السابقة.

مهمة:

var wildType = CONST_WILD_TYPES.REGULAR;

المقارنة:

if (wildType === CONST_WILD_TYPES.REGULAR) {
    // do something here
}

لقد قمت بتعديل حل Andre 'Fi':

  function Enum() {
    var that = this;
    for (var i in arguments) {
        that[arguments[i]] = i;
    }
    this.name = function(value) {
        for (var key in that) {
            if (that[key] == value) {
                return key;
            }
        }
    };
    this.exist = function(value) {
        return (typeof that.name(value) !== "undefined");
    };
    if (Object.freeze) {
        Object.freeze(that);
    }
  }

اختبار:

var Color = new Enum('RED', 'GREEN', 'BLUE');
undefined
Color.name(Color.REDs)
undefined
Color.name(Color.RED)
"RED"
Color.exist(Color.REDs)
false
Color.exist(Color.RED)
true

لقد قمت بعمل فئة التعداد التي يمكنها جلب القيم والأسماء في O (1). يمكنه أيضًا إنشاء Object Array الذي يحتوي على كافة الأسماء والقيم.

function Enum(obj) {
    // Names must be unique, Values do not.
    // Putting same values for different Names is risky for this implementation

    this._reserved = {
        _namesObj: {},
        _objArr: [],
        _namesArr: [],
        _valuesArr: [],
        _selectOptionsHTML: ""
    };

    for (k in obj) {
        if (obj.hasOwnProperty(k)) {
            this[k] = obj[k];
            this._reserved._namesObj[obj[k]] = k;
        }
    }
}
(function () {
    this.GetName = function (val) {
        if (typeof this._reserved._namesObj[val] === "undefined")
            return null;
        return this._reserved._namesObj[val];
    };

    this.GetValue = function (name) {
        if (typeof this[name] === "undefined")
            return null;
        return this[name];
    };

    this.GetObjArr = function () {
        if (this._reserved._objArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push({
                            Name: k,
                            Value: this[k]
                        });
            }
            this._reserved._objArr = arr;
        }
        return this._reserved._objArr;
    };

    this.GetNamesArr = function () {
        if (this._reserved._namesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(k);
            }
            this._reserved._namesArr = arr;
        }
        return this._reserved._namesArr;
    };

    this.GetValuesArr = function () {
        if (this._reserved._valuesArr.length == 0) {
            var arr = [];
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        arr.push(this[k]);
            }
            this._reserved._valuesArr = arr;
        }
        return this._reserved._valuesArr;
    };

    this.GetSelectOptionsHTML = function () {
        if (this._reserved._selectOptionsHTML.length == 0) {
            var html = "";
            for (k in this) {
                if (this.hasOwnProperty(k))
                    if (k != "_reserved")
                        html += "<option value='" + this[k] + "'>" + k + "</option>";
            }
            this._reserved._selectOptionsHTML = html;
        }
        return this._reserved._selectOptionsHTML;
    };
}).call(Enum.prototype);

يمكنك البدء بها على النحو التالي:

var enum1 = new Enum({
    item1: 0,
    item2: 1,
    item3: 2
});

لجلب قيمة (مثل Enums في C #):

var val2 = enum1.item2;

لجلب اسم لقيمة (يمكن أن يكون غامضًا عند وضع نفس القيمة لأسماء مختلفة):

var name1 = enum1.GetName(0);  // "item1"

للحصول على مصفوفة مع كل اسم وقيمة في كائن:

var arr = enum1.GetObjArr();

سوف يولد:

[{ Name: "item1", Value: 0}, { ... }, ... ]

يمكنك أيضًا الحصول على خيارات تحديد html بسهولة:

var html = enum1.GetSelectOptionsHTML();

الذي يحمل:

"<option value='0'>item1</option>..."

لقد لعبت مع هذا ، كما أحب التعداد الخاص بي. =)

باستخدام Object.defineProperty أعتقد أنني توصلت إلى حل قابل للبقاء إلى حد ما.

وإليك jsfiddle: http://jsfiddle.net/ZV4A6/

باستخدام هذه الطريقة .. يجب عليك (من الناحية النظرية) أن تكون قادرة على استدعاء وتعريف قيم التعداد لأي كائن ، دون التأثير على سمات أخرى لهذا الكائن.

Object.defineProperty(Object.prototype,'Enum', {
    value: function() {
        for(i in arguments) {
            Object.defineProperty(this,arguments[i], {
                value:parseInt(i),
                writable:false,
                enumerable:true,
                configurable:true
            });
        }
        return this;
    },
    writable:false,
    enumerable:false,
    configurable:false
}); 

بسبب السمة writable:false يجب أن يجعلها آمنة.

لذلك يجب أن تتمكن من إنشاء كائن مخصص ، ثم استدعاء Enum() عليه. تبدأ القيم المعينة عند 0 والزيادة لكل عنصر.

var EnumColors={};
EnumColors.Enum('RED','BLUE','GREEN','YELLOW');
EnumColors.RED;    // == 0
EnumColors.BLUE;   // == 1
EnumColors.GREEN;  // == 2
EnumColors.YELLOW; // == 3

هذا شيء قديم أعرفه ، ولكن الطريقة التي اتبعت من خلال واجهة TypeScript هي:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["Foo"] = 0] = "Foo";
    MyEnum[MyEnum["FooBar"] = 2] = "FooBar";
    MyEnum[MyEnum["Bar"] = 1] = "Bar";
})(MyEnum|| (MyEnum= {}));

يمكّنك هذا من البحث عن MyEnum.Bar الذي يقوم بإرجاع 1 و MyEnum[1] الذي يقوم بإرجاع "شريط" بغض النظر عن ترتيب تعريف.


هذا هو الحل الذي أستخدمه.

function Enum() {
    this._enums = [];
    this._lookups = {};
}

Enum.prototype.getEnums = function() {
    return _enums;
}

Enum.prototype.forEach = function(callback){
    var length = this._enums.length;
    for (var i = 0; i < length; ++i){
        callback(this._enums[i]);
    }
}

Enum.prototype.addEnum = function(e) {
    this._enums.push(e);
}

Enum.prototype.getByName = function(name) {
    return this[name];
}

Enum.prototype.getByValue = function(field, value) {
    var lookup = this._lookups[field];
    if(lookup) {
        return lookup[value];
    } else {
        this._lookups[field] = ( lookup = {});
        var k = this._enums.length - 1;
        for(; k >= 0; --k) {
            var m = this._enums[k];
            var j = m[field];
            lookup[j] = m;
            if(j == value) {
                return m;
            }
        }
    }
    return null;
}

function defineEnum(definition) {
    var k;
    var e = new Enum();
    for(k in definition) {
        var j = definition[k];
        e[k] = j;
        e.addEnum(j)
    }
    return e;
}

وتحدد التعدادات الخاصة بك على النحو التالي:

var COLORS = defineEnum({
    RED : {
        value : 1,
        string : 'red'
    },
    GREEN : {
        value : 2,
        string : 'green'
    },
    BLUE : {
        value : 3,
        string : 'blue'
    }
});

وهذه هي الطريقة التي تدخل بها التعدادات الخاصة بك:

COLORS.BLUE.string
COLORS.BLUE.value
COLORS.getByName('BLUE').string
COLORS.getByValue('value', 1).string

COLORS.forEach(function(e){
    // do what you want with e
});

عادة ما أستخدم آخر 2 طريقة لرسم التعدادات من كائنات الرسالة.

بعض المزايا لهذا النهج:

  • من السهل أن تعلن التعداد
  • من السهل الوصول إلى التعداد الخاص بك
  • يمكن أن يكون التعداد الخاص بك أنواعًا معقدة
  • يحتوي الصف Enum على بعض التخزين المؤقت النقابي إذا كنت تستخدم getByValue كثيرًا

بعض العيوب:

  • بعض إدارة الذاكرة الفوضوي مستمرة هناك ، حيث أحتفظ بالإشارات إلى التعدادات
  • لا يوجد حتى الآن نوع السلامة

يمكنك فعل شيء كهذا

function Enum(){
  this.add.apply(this,arguments);
}

Enum.prototype.add = function(){
  for (var i in arguments) {
    this[arguments[i]] = new String(arguments[i]);
  }
};
Enum.prototype.toList = function(){
  return Object.keys(this)
};

var STATUS = new Enum("CLOSED","PENDING");


var STATE = new Enum("CLOSED","PENDING");

STATE.CLOSED === STATUS.CLOSED  // false;
STATE.CLOSED === "CLOSED"  // false;
STATE.CLOSED.toString() === "CLOSED"  // true;

As defined in this library. https://github.com/webmodule/foo/blob/master/foo.js#L217


استكمال : شكرا لجميع upvotes الجميع ، لكنني لا أعتقد أن جوابي أدناه هو أفضل طريقة لكتابة enums في جافا سكريبت بعد الآن. شاهد منشور مدونتي لمزيد من التفاصيل: Enums في Javascript .

يكون التنبيه ممكنًا بالفعل:

if (currentColor == my.namespace.ColorEnum.RED) {
   // alert name of currentColor (RED: 0)
   var col = my.namespace.ColorEnum;
   for (var name in col) {
     if (col[name] == col.RED)
       alert(name);
   }
}

بدلا من ذلك ، يمكنك جعل قيم الكائنات ، بحيث يمكنك الحصول على الكعكة وأكلها أيضا:

var SIZE = {
  SMALL : {value: 0, name: "Small", code: "S"}, 
  MEDIUM: {value: 1, name: "Medium", code: "M"}, 
  LARGE : {value: 2, name: "Large", code: "L"}
};

var currentSize = SIZE.MEDIUM;
if (currentSize == SIZE.MEDIUM) {
  // this alerts: "1: Medium"
  alert(currentSize.value + ": " + currentSize.name);
}

في Javascript ، لأنها لغة ديناميكية ، من الممكن إضافة قيم التعداد إلى المجموعة التالية:

// Add EXTRALARGE size
SIZE.EXTRALARGE = {value: 3, name: "Extra Large", code: "XL"};

تذكر أن حقول التعداد (القيمة والاسم والتعليمات البرمجية في هذا المثال) ليست ضرورية لفحص الهوية وهي موجودة فقط من أجل الراحة. كما لا يحتاج اسم خاصية الحجم نفسه إلى ضمنية ، ولكن يمكن أيضًا تعيينه ديناميكيًا. لذا لنفترض أنك تعرف فقط اسم قيمة التعداد الجديدة ، فلا يزال بإمكانك إضافتها دون مشاكل:

// Add 'Extra Large' size, only knowing it's name
var name = "Extra Large";
SIZE[name] = {value: -1, name: name, code: "?"};

بالطبع هذا يعني أنه لم يعد من الممكن إجراء بعض الافتراضات (تلك القيمة تمثل الترتيب الصحيح للحجم على سبيل المثال).

تذكر ، في Javascript ، كائن مثل الخريطة أو hashtable. مجموعة من أزواج الاسم-القيمة. يمكنك التكرار عبرها أو التلاعب بها دون معرفة الكثير عنها مسبقًا.

EG:

for (var sz in SIZE) {
  // sz will be the names of the objects in SIZE, so
  // 'SMALL', 'MEDIUM', 'LARGE', 'EXTRALARGE'
  var size = SIZE[sz]; // Get the object mapped to the name in sz
  for (var prop in size) {
    // Get all the properties of the size object, iterates over
    // 'value', 'name' and 'code'. You can inspect everything this way.        
  }
} 

و btw ، إذا كنت مهتمًا بمساحات الأسماء ، فقد ترغب في إلقاء نظرة على حلّي لمساحة أسماء بسيطة وفعالة وإدارة قوية للارتباط بجافا سكريبت: Packages JS


As of writing, October 2014 - so here is a contemporary solution. Am writing the solution as a Node Module, and have included a test using Mocha and Chai, as well as underscoreJS. You can easily ignore these, and just take the Enum code if preferred.

Seen a lot of posts with overly convoluted libraries etc. The solution to getting enum support in Javascript is so simple it really isn't needed. هنا هو الرمز:

File: enums.js

_ = require('underscore');

var _Enum = function () {

   var keys = _.map(arguments, function (value) {
      return value;
   });
   var self = {
      keys: keys
   };
   for (var i = 0; i < arguments.length; i++) {
      self[keys[i]] = i;
   }
   return self;
};

var fileFormatEnum = Object.freeze(_Enum('CSV', 'TSV'));
var encodingEnum = Object.freeze(_Enum('UTF8', 'SHIFT_JIS'));

exports.fileFormatEnum = fileFormatEnum;
exports.encodingEnum = encodingEnum;

And a test to illustrate what it gives you:

file: enumsSpec.js

var chai = require("chai"),
    assert = chai.assert,
    expect = chai.expect,
    should = chai.should(),
    enums = require('./enums'),
    _ = require('underscore');


describe('enums', function () {

    describe('fileFormatEnum', function () {
        it('should return expected fileFormat enum declarations', function () {
            var fileFormatEnum = enums.fileFormatEnum;
            should.exist(fileFormatEnum);
            assert('{"keys":["CSV","TSV"],"CSV":0,"TSV":1}' === JSON.stringify(fileFormatEnum), 'Unexpected format');
            assert('["CSV","TSV"]' === JSON.stringify(fileFormatEnum.keys), 'Unexpected keys format');
        });
    });

    describe('encodingEnum', function () {
        it('should return expected encoding enum declarations', function () {
            var encodingEnum = enums.encodingEnum;
            should.exist(encodingEnum);
            assert('{"keys":["UTF8","SHIFT_JIS"],"UTF8":0,"SHIFT_JIS":1}' === JSON.stringify(encodingEnum), 'Unexpected format');
            assert('["UTF8","SHIFT_JIS"]' === JSON.stringify(encodingEnum.keys), 'Unexpected keys format');
        });
    });

});

As you can see, you get an Enum factory, you can get all the keys simply by calling enum.keys, and you can match the keys themselves to integer constants. And you can reuse the factory with different values, and export those generated Enums using Node's modular approach.

Once again, if you are just a casual user, or in the browser etc, just take the factory part of the code, potentially removing underscore library too if you don't wish to use it in your code.


I had done it a while ago using a mixture of __defineGetter__ and __defineSetter__ or defineProperty depending on the JS version.

Here's the enum generating function I made: https://gist.github.com/gfarrell/6716853

You'd use it like this:

var Colours = Enum('RED', 'GREEN', 'BLUE');

And it would create an immutable string:int dictionary (an enum).


I wrote enumerationjs a very tiny library to address the issue which ensures type safety , allow enum constants to inherit from a prototype , guaranties enum constants and enum types to be immutable + many little features. It allows to refactor a lot of code and move some logic inside the enum definition. Here is an example :

var CloseEventCodes = new Enumeration("closeEventCodes", {
  CLOSE_NORMAL:          { _id: 1000, info: "Connection closed normally" },
  CLOSE_GOING_AWAY:      { _id: 1001, info: "Connection closed going away" },
  CLOSE_PROTOCOL_ERROR:  { _id: 1002, info: "Connection closed due to protocol error"  },
  CLOSE_UNSUPPORTED:     { _id: 1003, info: "Connection closed due to unsupported operation" },
  CLOSE_NO_STATUS:       { _id: 1005, info: "Connection closed with no status" },
  CLOSE_ABNORMAL:        { _id: 1006, info: "Connection closed abnormally" },
  CLOSE_TOO_LARGE:       { _id: 1009, info: "Connection closed due to too large packet" }
},{ talk: function(){
    console.log(this.info); 
  }
});


CloseEventCodes.CLOSE_TOO_LARGE.talk(); //prints "Connection closed due to too large packet"
CloseEventCodes.CLOSE_TOO_LARGE instanceof CloseEventCodes //evaluates to true

Enumeration is basically a factory.

Fully documented guide available here. أتمنى أن يساعدك هذا.


You can try this:

   var Enum = Object.freeze({
            Role: Object.freeze({ Administrator: 1, Manager: 2, Supervisor: 3 }),
            Color:Object.freeze({RED : 0, GREEN : 1, BLUE : 2 })
            });

    alert(Enum.Role.Supervisor);
    alert(Enum.Color.GREEN);
    var currentColor=0;
    if(currentColor == Enum.Color.RED) {
       alert('Its Red');
    }

es7 way, (iterator, freeze), usage:

const ThreeWiseMen = new Enum('Melchior', 'Caspar', 'Balthazar')

for (let name of ThreeWiseMen)
    console.log(name)


// with a given key
let key = ThreeWiseMen.Melchior

console.log(key in ThreeWiseMen) // true (string conversion, also true: 'Melchior' in ThreeWiseMen)

for (let entry from key.enum)
     console.log(entry)


// prevent alteration (throws TypeError in strict mode)
ThreeWiseMen.Me = 'Me too!'
ThreeWiseMen.Melchior.name = 'Foo'

code:

class EnumKey {

    constructor(props) { Object.freeze(Object.assign(this, props)) }

    toString() { return this.name }

}

export class Enum {

    constructor(...keys) {

        for (let [index, key] of keys.entries()) {

            Object.defineProperty(this, key, {

                value: new EnumKey({ name:key, index, enum:this }),
                enumerable: true,

            })

        }

        Object.freeze(this)

    }

    *[Symbol.iterator]() {

        for (let key of Object.keys(this))
            yield this[key]

    }

    toString() { return [...this].join(', ') }

}




enums