以下内容是否会使对象满足枚举在JavaScript中的所有特性?类似于:

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

// later on

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

还是有其他方法可以做到这一点?


当前回答

这是我对一个(标记的)Enum工厂的看法。这是一个工作演示。

/*
 * Notes: 
 * The proxy handler enables case insensitive property queries
 * BigInt is used to enable bitflag strings /w length > 52
*/
function EnumFactory() {
  const proxyfy = {
    construct(target, args) { 
      const caseInsensitiveHandler = { 
          get(target, key) {
          return target[key.toUpperCase()] || target[key];  
        } 
      };
      const proxified = new Proxy(new target(...args), caseInsensitiveHandler ); 
      return Object.freeze(proxified);
    },
  }
  const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
  const throwIf = (
      assertion = false, 
      message = `Unspecified error`, 
      ErrorType = Error ) => 
      assertion && (() => { throw new ErrorType(message); })();
  const hasFlag = (val, sub) => {
    throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
    const andVal = (sub & val);
    return andVal !== BigInt(0) && andVal === val;
  };

  function EnumCtor(values) {
    throwIf(values.constructor !== Array || 
            values.length < 2 || 
        values.filter( v => v.constructor !== String ).length > 0,
      `EnumFactory: expected Array of at least 2 strings`, TypeError);
    const base = BigInt(1);
    this.NONE = BigInt(0);
    values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
  }

  EnumCtor.prototype = {
    get keys() { return Object.keys(this).slice(1); },
    subset(sub) {
      const arrayValues = this.keys;
      return new ProxiedEnumCtor(
        [...sub.toString(2)].reverse()
          .reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
      );
    },
    getLabel(enumValue) {
      const tryLabel = Object.entries(this).find( value => value[1] === enumValue );
      return !enumValue || !tryLabel.length ? 
        "getLabel: no value parameter or value not in enum" :
        tryLabel.shift();
    },
    hasFlag(val, sub = this) { return hasFlag(val, sub); },
  };
  
  return arr => new ProxiedEnumCtor(arr);
}

其他回答

这是我对一个(标记的)Enum工厂的看法。这是一个工作演示。

/*
 * Notes: 
 * The proxy handler enables case insensitive property queries
 * BigInt is used to enable bitflag strings /w length > 52
*/
function EnumFactory() {
  const proxyfy = {
    construct(target, args) { 
      const caseInsensitiveHandler = { 
          get(target, key) {
          return target[key.toUpperCase()] || target[key];  
        } 
      };
      const proxified = new Proxy(new target(...args), caseInsensitiveHandler ); 
      return Object.freeze(proxified);
    },
  }
  const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
  const throwIf = (
      assertion = false, 
      message = `Unspecified error`, 
      ErrorType = Error ) => 
      assertion && (() => { throw new ErrorType(message); })();
  const hasFlag = (val, sub) => {
    throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
    const andVal = (sub & val);
    return andVal !== BigInt(0) && andVal === val;
  };

  function EnumCtor(values) {
    throwIf(values.constructor !== Array || 
            values.length < 2 || 
        values.filter( v => v.constructor !== String ).length > 0,
      `EnumFactory: expected Array of at least 2 strings`, TypeError);
    const base = BigInt(1);
    this.NONE = BigInt(0);
    values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
  }

  EnumCtor.prototype = {
    get keys() { return Object.keys(this).slice(1); },
    subset(sub) {
      const arrayValues = this.keys;
      return new ProxiedEnumCtor(
        [...sub.toString(2)].reverse()
          .reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
      );
    },
    getLabel(enumValue) {
      const tryLabel = Object.entries(this).find( value => value[1] === enumValue );
      return !enumValue || !tryLabel.length ? 
        "getLabel: no value parameter or value not in enum" :
        tryLabel.shift();
    },
    hasFlag(val, sub = this) { return hasFlag(val, sub); },
  };
  
  return arr => new ProxiedEnumCtor(arr);
}

真的很像@Duncan上面所做的,但我不喜欢用Enum破坏全局Object函数空间,所以我写了以下内容:

function mkenum_1()
{
  var o = new Object();
  var c = -1;
  var f = function(e, v) { Object.defineProperty(o, e, { value:v, writable:false, enumerable:true, configurable:true })};

  for (i in arguments) {
    var e = arguments[i];
    if ((!!e) & (e.constructor == Object))
      for (j in e)
        f(j, (c=e[j]));
    else
      f(e, ++c);
    }

  return Object.freeze ? Object.freeze(o) : o;
}

var Sizes = mkenum_1('SMALL','MEDIUM',{LARGE: 100},'XLARGE');

console.log("MED := " + Sizes.MEDIUM);
console.log("LRG := " + Sizes.LARGE);

// Output is:
// MED := 1
// LRG := 100

@Stijin也有一个简洁的解决方案(参考他的博客),其中包括这些对象的财产。我也为此写了一些代码,接下来我会把它包括在内。

function mkenum_2(seed)
{
    var p = {};

    console.log("Seed := " + seed);

    for (k in seed) {
        var v = seed[k];

        if (v instanceof Array)
            p[(seed[k]=v[0])] = { value: v[0], name: v[1], code: v[2] };
        else
            p[v] = { value: v, name: k.toLowerCase(), code: k.substring(0,1) };
    }
    seed.properties = p;

    return Object.freeze ? Object.freeze(seed) : seed;
}

此版本生成了一个允许友好名称转换和短代码的附加属性列表。我喜欢这个版本,因为不需要像代码那样在财产中重复数据输入。

var SizeEnum2 = mkenum_2({ SMALL: 1, MEDIUM: 2, LARGE: 3});
var SizeEnum3 = mkenum_2({ SMALL: [1, "small", "S"], MEDIUM: [2, "medium", "M"], LARGE: [3, "large", "L"] });

这两者可以组合成一个处理单元mkenum(使用enum、分配值、创建和添加属性列表)。然而,由于我今天已经在这方面花了太多时间,我将把这个组合作为练习留给亲爱的读者。

在大多数现代浏览器中,有一种符号基元数据类型,可用于创建枚举。它将确保枚举的类型安全,因为JavaScript保证每个符号值都是唯一的,即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

外星人的解决方案是让事情尽可能简单:

使用enum关键字(在javascript中保留)如果enum关键字只是保留的,但未在javascript中实现,请定义以下内容constenumerate=spec=>spec.split(/\s*,\s*/).reduce((e,n)=>对象.assign(e,{[n]:n}),{})

现在,您可以轻松使用它

const kwords = enumerate("begin,end, procedure,if")
console.log(kwords, kwords.if, kwords.if == "if", kwords.undef)

我认为没有理由使枚举值显式变量。无论如何,脚本都是纯文本的,如果代码的一部分是字符串或有效代码,则没有任何区别。真正重要的是,无论何时使用或定义引号,都不需要处理成吨的引号。

这不是一个很好的答案,但我个人认为这很好

话虽如此,因为值是什么并不重要(您使用了0、1、2),所以我会使用一个有意义的字符串,以防您想要输出当前值。