【问题标题】:Enum flags in JavaScriptJavaScript 中的枚举标志
【发布时间】:2010-12-10 05:19:09
【问题描述】:

我需要在 Javascript 中模拟 enum 类型,而且方法看起来很简单:

var MyEnum = {Left = 1; Right = 2; Top = 4; Bottom = 8}

现在,在 C# 中,我可以像这样组合这些值:

MyEnum left_right = MyEnum.Left | MyEnum.Right

然后我可以测试枚举是否具有一定的价值:

if (left_right & MyEnum.Left == MyEnum.Left) {...}

我可以在 Javascript 中做类似的事情吗?

【问题讨论】:

  • 请注意,MyEnum 的对象语法是错误的。 CMS 已经在他的回答中提供了一个更正的示例。

标签: javascript enums flags


【解决方案1】:

您只需要使用bitwise 运算符:

var myEnum = {
  left: 1,
  right: 2,
  top: 4,
  bottom: 8
}

var myConfig = myEnum.left | myEnum.right;

if (myConfig & myEnum.right) {
  // right flag is set
}

更多信息:

【讨论】:

    【解决方案2】:

    在 javascript 中,您应该能够将它们组合为:

    var left_right = MyEnum.Left | MyEnum.Right;
    

    那么测试将与您的示例完全相同

    if ( (left_right & MyEnum.Left) == MyEnum.Left) {...}
    

    【讨论】:

      【解决方案3】:

      是的,按位算术在 Javascript 中有效。你必须小心它,因为 Javascript 只有 Number 数据类型,它是作为浮点类型实现的。但是,对于按位运算,值会转换为 有符号 32 位值。因此,只要您不尝试使用超过 31 位,就可以了。

      【讨论】:

        【解决方案4】:

        我尝试创建一个示例来演示一个常见用例,在该用例中,人们可能希望使用位掩码枚举来控制日志记录的详细程度。它演示了 JavaScript 按位操作的用法:See it on JSFiddle

        /*
         * Demonstration of how a Flags enum can be simulated in JavaScript and 
         * Used to control what gets logged based on user passed value
         */
        
        // A Flags Enum (sort-of)
        var LogLevels = {
            NONE: 0,
            INFO: 1,
            TRACE: 2,
            DEBUG: 4,
            WARN: 8,
            ERROR: 16,
            FATAL: 32
        };
        
        // Initialize
        var currLogLevel = LogLevels.NONE;
        
        // User Sets a log level
        var logLevel = LogLevels.WARN;
        
        // Convert the configured logLvel to a bit-masked enum value
        switch (logLevel) {
            case LogLevels.INFO:
                currLogLevel = LogLevels.INFO | LogLevels.TRACE | LogLevels.DEBUG | LogLevels.WARN | LogLevels.ERROR | LogLevels.FATAL;
                break;
            case LogLevels.TRACE:
                currLogLevel = LogLevels.TRACE | LogLevels.DEBUG | LogLevels.WARN | LogLevels.ERROR | LogLevels.FATAL;
                break;
            case LogLevels.DEBUG:
                currLogLevel = LogLevels.DEBUG | LogLevels.WARN | LogLevels.ERROR | LogLevels.FATAL;
                break;
            case LogLevels.WARN:
                currLogLevel = LogLevels.WARN | LogLevels.ERROR | LogLevels.FATAL;
                break;
            case LogLevels.ERROR:
            case LogLevels.FATAL:
            default:
                currLogLevel = LogLevels.ERROR | LogLevels.FATAL;
        }
        
        // Example: log verbosity set to WARN, so this would NOT be logged
        if ((currLogLevel & LogLevels.DEBUG) == LogLevels.DEBUG) {
            console.log("log DEBUG");
        }
        

        【讨论】:

          【解决方案5】:

          打字稿中有我的实现:

          export class FlagEnumService {
          
              constructor(private value: number = 0) {
              }
          
              public get() {
                  return this.value;
              }
          
              public set(value: number): this {
                  this.value = value;
                  return this;
              }
          
              public has(key: number): boolean {
                  return !!(this.value & key);
              }
          
              public add(key: number): this {
                  this.value |= key;
                  return this;
              }
          
              public delete(key: number): this {
                  this.value &= ~key;
                  return this;
              }
          
              public toggle(key: number): this {
                  this.has(key) ? this.delete(key) : this.add(key);
                  return this;
              }
          }
          

          并进行澄清测试

          import { FlagEnumService } from './flag-enum.service';
          
          enum Test {
              None = 0,
              First = 1,
              Second = 2,
              Third = 4,
              Four = 8
          }
          
          describe('FlagEnumService', () => {
              let service: FlagEnumService;
              beforeEach(() => service = new FlagEnumService());
          
              it('should create with initial value', () => {
                  service = new FlagEnumService(Test.First);
                  expect(service.get()).toBe(Test.First);
              });
          
              it('should return true if has flag', () => {
                  service = new FlagEnumService(Test.First);
                  expect(service.has(Test.First)).toBe(true);
              });
          
              it('should return false if doesn\'t have flag', () => {
                  service = new FlagEnumService(Test.First);
                  expect(service.has(Test.Second)).toBe(false);
              });
          
              it('should add', () => {
                  expect(service.add(Test.First).add(Test.Second).get()).toBe(Test.First + Test.Second);
              });
          
              it('should not add the same value twice', () => {
                  expect(service.add(Test.First).add(Test.First).get()).toBe(Test.First);
              });
          
              it('should remove', () => {
                  expect(
                      service
                          .add(Test.First)
                          .add(Test.Second)
                          .delete(Test.Second)
                          .get()
                  )
                      .toBe(Test.First);
              });
          
              it('should return 0 when add then remove the same value', () => {
                  expect(service.add(Test.First).delete(Test.First).get()).toBe(0);
              });
          
              it('should not remove not added values', () => {
                  expect(service.add(Test.First).delete(Test.Second).get()).toBe(Test.First);
              });
          });
          

          【讨论】:

            【解决方案6】:

            JavaScript 中的标志枚举

            标志枚举:值必须以 2 的幂递增

            var SEASONS = {
              Spring : 1,
              Summer : 2,
              Fall   : 4,
              Winter : 8
            };
            

            不能在按位 & 操作中使用 0 来测试标志 b/c 它会 总是导致零。但是,它可以用于逻辑比较。

            用法示例(人为)

            var getSeasonsSelected = function( seasons ) {
              var selected = [];
            
              // The perens are needed around the bitwise operation due to the
              // greater operator precedence of `===`
              if ( (seasons & SEASONS.Spring) === SEASONS.Spring ) selected.push('Spring');
              if ( (seasons & SEASONS.Summer) === SEASONS.Summer ) selected.push('Summer');
              if ( (seasons & SEASONS.Fall)   === SEASONS.Fall )   selected.push('Fall');
              if ( (seasons & SEASONS.Winter) === SEASONS.Winter ) selected.push('Winter');
            
              return selected;
            };
            
            var s1 = getSeasonsSelected( SEASONS.Spring | SEASONS.Fall );
            console.log(s1);
            //=> ["Spring", "Fall"]
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多