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




كتاب بحث (25)

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

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

// later on

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

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


Answers

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');
    }

var ColorEnum = {
    red: {},
    green: {},
    blue: {}
}

You don't need to make sure you don't assign duplicate numbers to different enum values this way. A new object gets instantiated and assigned to all enum values.


It's easy to use, I think. https://.com/a/32245370/4365315

var A = {a:11, b:22}, 
enumA = new TypeHelper(A);

if(enumA.Value === A.b || enumA.Key === "a"){ 
... 
}

var keys = enumA.getAsList();//[object, object]

//set
enumA.setType(22, false);//setType(val, isKey)

enumA.setType("a", true);

enumA.setTypeByIndex(1);

تحديث:

There is my helper codes( TypeHelper ).

var Helper = {
    isEmpty: function (obj) {
        return !obj || obj === null || obj === undefined || Array.isArray(obj) && obj.length === 0;
    },

    isObject: function (obj) {
        return (typeof obj === 'object');
    },

    sortObjectKeys: function (object) {
        return Object.keys(object)
            .sort(function (a, b) {
                c = a - b;
                return c
            });
    },
    containsItem: function (arr, item) {
        if (arr && Array.isArray(arr)) {
            return arr.indexOf(item) > -1;
        } else {
            return arr === item;
        }
    },

    pushArray: function (arr1, arr2) {
        if (arr1 && arr2 && Array.isArray(arr1)) {
            arr1.push.apply(arr1, Array.isArray(arr2) ? arr2 : [arr2]);
        }
    }
};
function TypeHelper() {
    var _types = arguments[0],
        _defTypeIndex = 0,
        _currentType,
        _value,
        _allKeys = Helper.sortObjectKeys(_types);

    if (arguments.length == 2) {
        _defTypeIndex = arguments[1];
    }

    Object.defineProperties(this, {
        Key: {
            get: function () {
                return _currentType;
            },
            set: function (val) {
                _currentType.setType(val, true);
            },
            enumerable: true
        },
        Value: {
            get: function () {
                return _types[_currentType];
            },
            set: function (val) {
                _value.setType(val, false);
            },
            enumerable: true
        }
    });
    this.getAsList = function (keys) {
        var list = [];
        _allKeys.forEach(function (key, idx, array) {
            if (key && _types[key]) {

                if (!Helper.isEmpty(keys) && Helper.containsItem(keys, key) || Helper.isEmpty(keys)) {
                    var json = {};
                    json.Key = key;
                    json.Value = _types[key];
                    Helper.pushArray(list, json);
                }
            }
        });
        return list;
    };

    this.setType = function (value, isKey) {
        if (!Helper.isEmpty(value)) {
            Object.keys(_types).forEach(function (key, idx, array) {
                if (Helper.isObject(value)) {
                    if (value && value.Key == key) {
                        _currentType = key;
                    }
                } else if (isKey) {
                    if (value && value.toString() == key.toString()) {
                        _currentType = key;
                    }
                } else if (value && value.toString() == _types[key]) {
                    _currentType = key;
                }
            });
        } else {
            this.setDefaultType();
        }
        return isKey ? _types[_currentType] : _currentType;
    };

    this.setTypeByIndex = function (index) {
        for (var i = 0; i < _allKeys.length; i++) {
            if (index === i) {
                _currentType = _allKeys[index];
                break;
            }
        }
    };

    this.setDefaultType = function () {
        this.setTypeByIndex(_defTypeIndex);
    };

    this.setDefaultType();
}

var TypeA = {
    "-1": "Any",
    "2": "2L",
    "100": "100L",
    "200": "200L",
    "1000": "1000L"
};

var enumA = new TypeHelper(TypeA, 4);

document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setType("200L", false);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

enumA.setDefaultType();
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");


enumA.setTypeByIndex(1);
document.writeln("Key = ", enumA.Key,", Value = ", enumA.Value, "<br>");

document.writeln("is equals = ", (enumA.Value == TypeA["2"]));


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(', ') }

}

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

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


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

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

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

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');

لا يدعم 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
}

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

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 كثيرًا

بعض العيوب:

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

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. أتمنى أن يساعدك هذا.


لقد قمت بعمل فئة التعداد التي يمكنها جلب القيم والأسماء في 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>..."

هذا ليس جوابًا كبيرًا ، لكنني أقول أنه يعمل بشكل جيد ، شخصيًا

بعد قولي هذا ، بما أنه لا يهم ما هي القيم (التي استخدمتها 0 ، 1 ، 2) ، فسوف أستخدم سلسلة ذات معنى في حال أردت إخراج القيمة الحالية.


استكمال : شكرا لجميع 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


في ES7 ، يمكنك إجراء ENUM أنيق بالاعتماد على السمات الثابتة:

class ColorEnum  {
    static RED = 0 ;
    static GREEN = 1;
    static BLUE = 2;
}

ثم

if (currentColor === ColorEnum.GREEN ) {/*-- coding --*/}

ميزة (استخدام فئة بدلاً من كائن حرفية) هو أن يكون لديك فئة أصل Enum ثم سيتم توسيع كافة التعدادات الخاصة بك هذا الصف.

 class ColorEnum  extends Enum {/*....*/}

توصلت إلى this النهج الذي تم تصميمه على غرار التعدادات في جافا. هذه هي آمنة من نوع ، ولذا يمكنك إجراء فحوصات المراجعات أيضًا.

يمكنك تعريف enums مثل هذا:

var Days = Enum.define("Days", ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]);

تشير Days الآن إلى تعداد Days :

Days.Monday instanceof Days; // true

Days.Friday.name(); // "Friday"
Days.Friday.ordinal(); // 4

Days.Sunday === Days.Sunday; // true
Days.Sunday === Days.Friday; // false

Days.Sunday.toString(); // "Sunday"

Days.toString() // "Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } "

Days.values().map(function(e) { return e.name(); }); //["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Days.values()[4].name(); //"Friday"

Days.fromName("Thursday") === Days.Thursday // true
Days.fromName("Wednesday").name() // "Wednesday"
Days.Friday.fromName("Saturday").name() // "Saturday"

التطبيق:

var Enum = (function () {
    /**
     * Function to define an enum
     * @param typeName - The name of the enum.
     * @param constants - The constants on the enum. Can be an array of strings, or an object where each key is an enum
     * constant, and the values are objects that describe attributes that can be attached to the associated constant.
     */
    function define(typeName, constants) {

        /** Check Arguments **/
        if (typeof typeName === "undefined") {
            throw new TypeError("A name is required.");
        }

        if (!(constants instanceof Array) && (Object.getPrototypeOf(constants) !== Object.prototype)) {

            throw new TypeError("The constants parameter must either be an array or an object.");

        } else if ((constants instanceof Array) && constants.length === 0) {

            throw new TypeError("Need to provide at least one constant.");

        } else if ((constants instanceof Array) && !constants.reduce(function (isString, element) {
                return isString && (typeof element === "string");
            }, true)) {

            throw new TypeError("One or more elements in the constant array is not a string.");

        } else if (Object.getPrototypeOf(constants) === Object.prototype && !Object.keys(constants).reduce(function (isObject, constant) {
                return Object.getPrototypeOf(constants[constant]) === Object.prototype;
            }, true)) {

            throw new TypeError("One or more constants do not have an associated object-value.");

        }

        var isArray = (constants instanceof Array);
        var isObject = !isArray;

        /** Private sentinel-object used to guard enum constructor so that no one else can create enum instances **/
        function __() { };

        /** Dynamically define a function with the same name as the enum we want to define. **/
        var __enum = new Function(["__"],
            "return function " + typeName + "(sentinel, name, ordinal) {" +
                "if(!(sentinel instanceof __)) {" +
                    "throw new TypeError(\"Cannot instantiate an instance of " + typeName + ".\");" +
                "}" +

                "this.__name = name;" +
                "this.__ordinal = ordinal;" +
            "}"
        )(__);

        /** Private objects used to maintain enum instances for values(), and to look up enum instances for fromName() **/
        var __values = [];
        var __dict = {};

        /** Attach values() and fromName() methods to the class itself (kind of like static methods). **/
        Object.defineProperty(__enum, "values", {
            value: function () {
                return __values;
            }
        });

        Object.defineProperty(__enum, "fromName", {
            value: function (name) {
                var __constant = __dict[name]
                if (__constant) {
                    return __constant;
                } else {
                    throw new TypeError(typeName + " does not have a constant with name " + name + ".");
                }
            }
        });

        /**
         * The following methods are available to all instances of the enum. values() and fromName() need to be
         * available to each constant, and so we will attach them on the prototype. But really, they're just
         * aliases to their counterparts on the prototype.
         */
        Object.defineProperty(__enum.prototype, "values", {
            value: __enum.values
        });

        Object.defineProperty(__enum.prototype, "fromName", {
            value: __enum.fromName
        });

        Object.defineProperty(__enum.prototype, "name", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "ordinal", {
            value: function () {
                return this.__ordinal;
            }
        });

        Object.defineProperty(__enum.prototype, "valueOf", {
            value: function () {
                return this.__name;
            }
        });

        Object.defineProperty(__enum.prototype, "toString", {
            value: function () {
                return this.__name;
            }
        });

        /**
         * If constants was an array, we can the element values directly. Otherwise, we will have to use the keys
         * from the constants object.
         */
        var _constants = constants;
        if (isObject) {
            _constants = Object.keys(constants);
        }

        /** Iterate over all constants, create an instance of our enum for each one, and attach it to the enum type **/
        _constants.forEach(function (name, ordinal) {
            // Create an instance of the enum
            var __constant = new __enum(new __(), name, ordinal);

            // If constants was an object, we want to attach the provided attributes to the instance.
            if (isObject) {
                Object.keys(constants[name]).forEach(function (attr) {
                    Object.defineProperty(__constant, attr, {
                        value: constants[name][attr]
                    });
                });
            }

            // Freeze the instance so that it cannot be modified.
            Object.freeze(__constant);

            // Attach the instance using the provided name to the enum type itself.
            Object.defineProperty(__enum, name, {
                value: __constant
            });

            // Update our private objects
            __values.push(__constant);
            __dict[name] = __constant;
        });

        /** Define a friendly toString method for the enum **/
        var string = typeName + " { " + __enum.values().map(function (c) {
                return c.name();
            }).join(", ") + " } ";

        Object.defineProperty(__enum, "toString", {
            value: function () {
                return string;
            }
        });

        /** Freeze our private objects **/
        Object.freeze(__values);
        Object.freeze(__dict);

        /** Freeze the prototype on the enum and the enum itself **/
        Object.freeze(__enum.prototype);
        Object.freeze(__enum);

        /** Return the enum **/
        return __enum;
    }

    return {
        define: define
    }

})();

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

باستخدام 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

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

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

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"

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).


Simplest solution:

خلق

var Status = Object.freeze({
    "Connecting":0,
    "Ready":1,
    "Loading":2,
    "Processing": 3
});

Get Value

console.log(Status.Ready) // 1

Get Key

console.log(Object.keys(Status)[Status.Ready]) // Ready

إليك طريقتان مختلفتان لتطبيق enScript TypeScript .

أسهل طريقة هي فقط للتكرار على كائن ، مضيفا أزواج قيمة مفتاح مقلوب إلى الكائن. العيب الوحيد هو أنه يجب عليك تعيين القيمة يدويًا لكل عضو.

function _enum(list) {       
  for (var key in list) {
    list[list[key] = list[key]] = key;
  }
  return Object.freeze(list);
}

var Color = _enum({
  Red: 0,
  Green: 5,
  Blue: 2
});

// Color → {0: "Red", 2: "Blue", 5: "Green", "Red": 0, "Green": 5, "Blue": 2}
// Color.Red → 0
// Color.Green → 5
// Color.Blue → 2
// Color[5] → Green
// Color.Blue > Color.Green → false


وإليك mixond لإنشاء تعداد باستخدام سلسلة. في حين أن هذا الإصدار أكثر انخفاضاً ، فإنه يقوم بالترقيم تلقائيًا بالنسبة لك. جميع أساليب towash المستخدمة في هذا المثال لديها ما يعادل جافا سكريبت العادية ، لذلك يمكنك تبديلها بسهولة إذا كنت تريد.

function enum() {
    var key, val = -1, list = {};
    _.reduce(_.toArray(arguments), function(result, kvp) {    
        kvp = kvp.split("=");
        key = _.trim(kvp[0]);
        val = _.parseInt(kvp[1]) || ++val;            
        result[result[val] = key] = val;
        return result;
    }, list);
    return Object.freeze(list);
}    

// Add enum to lodash 
_.mixin({ "enum": enum });

var Color = _.enum(
    "Red",
    "Green",
    "Blue = 5",
    "Yellow",
    "Purple = 20",
    "Gray"
);

// Color.Red → 0
// Color.Green → 1
// Color.Blue → 5
// Color.Yellow → 6
// Color.Purple → 20
// Color.Gray → 21
// Color[5] → Blue

في معظم المتصفحات الحديثة ، هناك نوع بيانات 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

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

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

Even though only static methods (and not static properties) are supported in ES2015 (see here as well, §15.2.2.2), curiously you can use the below with Babel with the es2015 preset:

class CellState {
    v: string;
    constructor(v: string) {
        this.v = v;
        Object.freeze(this);
    }
    static EMPTY       = new CellState('e');
    static OCCUPIED    = new CellState('o');
    static HIGHLIGHTED = new CellState('h');
    static values      = function(): Array<CellState> {
        const rv = [];
        rv.push(CellState.EMPTY);
        rv.push(CellState.OCCUPIED);
        rv.push(CellState.HIGHLIGHTED);
        return rv;
    }
}
Object.freeze(CellState);

I found this to be working as expected even across modules (eg importing the CellState enum from another module) and also when I import a module using Webpack.

The advantage this method has over most other answers is that you can use it alongside a static type checker (eg Flow ) and you can assert, at development time using static type checking, that your variables, parameters, etc. are of the specific CellState "enum" rather than some other enum (which would be impossible to distinguish if you used generic objects or symbols).

update

The above code has a deficiency in that it allows one to create additional objects of type CellState (even though one can't assign them to the static fields of CellState since it's frozen). Still, the below more refined code offers the following advantages:

  1. no more objects of type CellState may be created
  2. you are guaranteed that no two enum instances are assigned the same code
  3. utility method to get the enum back from a string representation
  4. the values function that returns all instances of the enum does not have to create the return value in the above, manual (and error-prone) way.

    'use strict';
    
    class Status {
    
    constructor(code, displayName = code) {
        if (Status.INSTANCES.has(code))
            throw new Error(`duplicate code value: [${code}]`);
        if (!Status.canCreateMoreInstances)
            throw new Error(`attempt to call constructor(${code}`+
           `, ${displayName}) after all static instances have been created`);
        this.code        = code;
        this.displayName = displayName;
        Object.freeze(this);
        Status.INSTANCES.set(this.code, this);
    }
    
    toString() {
        return `[code: ${this.code}, displayName: ${this.displayName}]`;
    }
    static INSTANCES   = new Map();
    static canCreateMoreInstances      = true;
    
    // the values:
    static ARCHIVED    = new Status('Archived');
    static OBSERVED    = new Status('Observed');
    static SCHEDULED   = new Status('Scheduled');
    static UNOBSERVED  = new Status('Unobserved');
    static UNTRIGGERED = new Status('Untriggered');
    
    static values      = function() {
        return Array.from(Status.INSTANCES.values());
    }
    
    static fromCode(code) {
        if (!Status.INSTANCES.has(code))
            throw new Error(`unknown code: ${code}`);
        else
            return Status.INSTANCES.get(code);
    }
    }
    
    Status.canCreateMoreInstances = false;
    Object.freeze(Status);
    exports.Status = Status;
    

أوصي بشدة كل مطور لبدء استخدام الوضع الصارم الآن. هناك ما يكفي من المتصفحات التي تدعمها أن الوضع المقيد سيساعد بشكل شرعي في حمايتنا من الأخطاء التي لم نكن نعرف أنها كانت في شفرتك.

على ما يبدو ، في المرحلة الأولى ستكون هناك أخطاء لم نواجهها من قبل. للحصول على الفائدة الكاملة ، نحتاج إلى إجراء الاختبار المناسب بعد التبديل إلى الوضع المقيد للتأكد من أننا قد أمسكنا بكل شيء. من المؤكد أننا لا نرمي use strict في الشفرة ونفترض عدم وجود أخطاء. لذا ، فإن العزم هو أن الوقت قد حان للبدء في استخدام ميزة اللغة المفيدة هذه بشكل لا يصدق لكتابة رمز أفضل.

فمثلا،

var person = {
    name : 'xyz',
    position : 'abc',
    fullname : function () {  "use strict"; return this.name; }
};

JSLint هو مصحح كتب بواسطة Douglas Crockford. ما عليك سوى لصق النص في النص البرمجي ، وسيجري مسحًا سريعًا لأية مشكلات أو أخطاء ملحوظة في شفرتك.







javascript enums