JavaScript 中的枚举标志

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

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

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

MyEnum left_right = MyEnum.Left | MyEnum.Right

然后我可以测试 enum 是否有一定的值:

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

我能用 Javascript 做这样的事情吗?

48713 次浏览

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

var left_right = MyEnum.Left | MyEnum.Right;

那么测试将与您的示例中的一模一样

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

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

你只需要使用 按位计算运算符:

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


var myConfig = myEnum.left | myEnum.right;


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

更多信息:

我试图创建一个示例来演示一个常见用例,其中可能需要使用位掩码枚举来控制日志详细度。它演示了 JavaScript 的位操作: 参见 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");
}

这是我在打字机中的实现:

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

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"]