Enum flags in JavaScript

前端 未结 6 445
夕颜
夕颜 2021-01-30 08:23

I need to emulate enum type in Javascript and approach seems pretty straight forward:

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

N

相关标签:
6条回答
  • 2021-01-30 08:58

    You just have to use the bitwise operators:

    var myEnum = {
      left: 1,
      right: 2,
      top: 4,
      bottom: 8
    }
    
    var myConfig = myEnum.left | myEnum.right;
    
    if (myConfig & myEnum.right) {
      // right flag is set
    }
    

    More info:

    • Understanding bitwise operations in javascript
    • How to check my byte flag?
    0 讨论(0)
  • 2021-01-30 08:58

    Yes, bitwise arithmetic works in Javascript. You have to be careful with it because Javascript only has the Number data type, which is implemented as a floating-point type. But, values are converted to signed 32-bit values for bitwise operations. So as long as you don't try to use more than 31 bits, you'll be fine.

    0 讨论(0)
  • 2021-01-30 09:01

    In javascript you should be able to combine them as:

    var left_right = MyEnum.Left | MyEnum.Right;
    

    Then testing would be exactly as it is in your example of

    if ( (left_right & MyEnum.Left) == MyEnum.Left) {...}
    
    0 讨论(0)
  • 2021-01-30 09:09

    I've tried to create an example that demonstrates a common use case where one may want to use bit mask enums to control logging verbosity. It demonstrates the us of JavaScript bit-wise operations: 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");
    }
    
    0 讨论(0)
  • 2021-01-30 09:09

    Flag Enumerations in JavaScript

    Flag enums: Values must increment by powers of 2

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

    Cannot use 0 in a bitwise & operation to test for a flag b/c it will always result in zero. However, it can be used for logical comparisons.

    Usage examples (contrived)

    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"]
    
    0 讨论(0)
  • 2021-01-30 09:15

    There is my implementation in typescript:

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

    And tests for clarification

    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);
        });
    });
    
    0 讨论(0)
提交回复
热议问题