JavaScript, определяющий неизменяемые перечисления

Итак, я был вдохновлен этим вопросом ( Перечисления в JavaScript?), Чтобы приступить к работе над вставкой библиотеки для JavaScript, чтобы включить немодифицируемые перечисления. У меня уже есть прилично работающий метод, но я бы хотел еще больше его уточнить.

Эта концепция использует Object.defineProperty (Документация: здесь)

Мое текущее определение позволяет:

var obj1 = {}, obj2 = {}, obj3 = {};
// Straight declaration (normal)
obj1.Enum('RED','BLUE','GREEN');
obj1.RED    // == 0
obj1.BLUE   // == 1
obj1.GREEN  // == 2
// Buffer (padding) usage
obj2.Enum('RED','BLUE',null,undefined,'','GREEN');
obj2.RED    // == 0
obj2.BLUE   // == 1
obj2.GREEN  // == 5
// Direct offset and case-correction
obj3.Enum('RED','BLUE',10,'gReEn','burnt orange');
obj3.RED           // == 0
obj3.BLUE          // == 1
obj3.GREEN         // == 11
obj3.BURNT_ORANGE  // == 12

Что у меня так далеко:

var odp=Object.defineProperty;
odp(Object.prototype,'Enum', {
    value: function() {
        var ignore=[undefined,null,''], n=0, args=arguments;
        for(var i in args) {
            if(ignore.indexOf(args[i])<0) {
                if( typeof args[i]=="number") {
                    n=parseInt(args[i]);
                } else {
                    try {
                        odp(this,String(args[i]).toUpperCase().replace(" ","_"), {
                            value:parseInt(n),enumerable:true
                        });
                    } catch(e) {
                        console.warn(e.message);
                        n--;
                    }
                }
            }
            n++;
        }
        return this;
    }
});

2 вещи, которые я хотел бы добавить к этому:

  1. Поддержка старых браузеров: переопределение Object.defineProperty где это не определено. (В настоящее время у меня нет доступа к более старым браузерам для проверки возможных переопределений)
  2. Любые соображения, которые я мог пропустить это определение Enum.

jsFiddle:

Примечание: причина у меня есть odp=Object.defineProperty а также args=arguments в том, что я запускаю свой JavaScript через компилятор замыкания перед тем, как вставить его в мои страницы, что помогает с компрессией. (для тех, кому это может быть интересно)

1 ответ

Решение

Поддержка старых браузеров: переопределение Object.defineProperty где это не определено.

Вы не можете сделать это с вашим текущим подходом, потому что ваш текущий подход расширяется Object.prototype, простирающийся Object.prototype должно быть очень, очень редко, если у вас есть поддержка ES5, но это никогда не должно быть сделано без нее, потому что без Object.defineProperty, у вас нет возможности создать не перечисляемое свойство. Добавление перечислимых свойств в Object.property сломает все ваши for-in петли.

Там нет причин для Enum в любом случае, чтобы быть на прототипе, просто наденьте его Object (или ваш собственный объект библиотеки) и передайте в него объект для перечисления.

Любые соображения, которые я мог пропустить это определение Enum.

Мне кажется, что это предлагает немного сверху:

var obj = Object.freeze({
    "RED":   0,
    "GREEN": 1,
    "BLUE":  2
});

Вы можете заключить это в функцию, которая будет работать без замораживания объекта в средах до ES5, например:

function createEnum(spec) {
    if (Object.freeze) {
        spec = Object.freeze(spec);
    }
    return spec;
}

Конечно, вы можете улучшить это, если хотите использовать автоматические значения, например:

function createEnum(spec) {
    var obj = {}, n;
    if (Object.prototype.toString.call(spec) === "[object Array]") { // No isArray pre ES5
        for (n = 0; n < spec.length; ++n) {
            if (spec[n] != null) { // checks for null or undefined
                obj[spec[n]] = n;
            }
        }
    }
    else {
        for (n in spec) {
            if (spec.hasOwnProperty(n)) {
                obj[n] = spec[n];
            }
        }
    }
    if (Object.freeze) {
        obj = Object.freeze(obj);
    }
    return obj;
}

Вы можете поставить это на Object (не Object.prototype), если вам понравилось.


Ниже вы сказали, что не хотите использовать freeze потому что это делает объект нерасширяемым. Достаточно справедливо, желая добавить дополнительные значения перечисления позже. Вышесказанное легко приспособить для этого, используя Object.defineProperty если присутствует и возвращается к назначению свойства, если нет.

Другие вопросы по тегам