Why exponential enum keys are used here?

a 夏天 提交于 2020-02-24 15:00:13

问题


Here you can find this code:

enum NodeFlags {
    None = 0,
    Let = 1,
    Const = 2,
    NestedNamespace = 4,
    Synthesized = 8,
    Namespace = 16,
    ExportContext = 32,
    ContainsThis = 64,

And I've seen such numeric constants in other places too. What is the purpose of this?

My guess was that it is done to add some new items later somewhere between the existing ones. Especially after seeing this:

    ThisNodeHasError = 32768,
    JavaScriptFile = 65536,
    ThisNodeOrAnySubNodesHasError = 131072,
    HasAggregatedChildData = 262144,
    JSDoc = 1048576,
    BlockScoped = 3, // three?

But isn't it in this case easier to have numbers like 100, 200, 300 etc.

The other guess was that it has something to do with bitwise operations, but I am not sure at all after I saw 3.

FWIW, the list ends up completely weirdly:

    ReachabilityCheckFlags = 384,
    ReachabilityAndEmitFlags = 1408,
    ContextFlags = 6387712,
    TypeExcludesFlags = 20480,

P.S. Probably exponential is not correct here (so I am sorry and you are welcomed to correct me).


回答1:


The reason is that this enum is used as a flags enum. These values can be combined using the bitwise or (|) operator. So you can have a value that is simultaneously both memebers of the enum

let x = NodeFlags.AwaitContext  | NodeFlags.ThisNodeHasError // The node is both an await context but also has errors

For this to work the values must not interfere with each other at a bit level, so each value must be a power of two (which will only have a single bit set to one at a different position for each power)

Basically each bit in the value is an independent flag. They could have gone with separate flags on the node such as isLet, isConst, isAwaitContext, isError etc, but that would have been wasteful in terms of memory and for a compiler that adds up since there are a lot of nodes. Like this a single field of 64 bits can represent 64 flags.

To extract which value is set you can use the & operator for example x & NodeFlags.ThisNodeHasError !== 0 would mean the ThisNodeHasError is set in the x variable.




回答2:


With a combined flag, you could get the enum types by checking the values with a bitwise AND &.

This works in the other direction as well where you could just add all flags with bitwise OR |

const getFlags = value => Object.keys(nodeFlags).filter(k => nodeFlags[k] & value);

var nodeFlags = { None: 0, Let: 1, Const: 2, NestedNamespace: 4, Synthesized: 8, Namespace: 16, ExportContext: 32, ContainsThis: 64 },
    blockScoped = 3,
    flagsOfBlockScoped = getFlags(blockScoped);
    
console.log(flagsOfBlockScoped);



回答3:


These are all powers of two, so in binary, you'd have

None = 0b0,
Let = 0b1,
Const = 0b10,
NestedNamespace = 0b100,
Synthesized = 0b1000,
Namespace = 0b10000,

And so on. This makes it possible to combine the flags, for example 111 means Let, Const, NestedNamespace

In your case ReachabilityCheckFlags = 384 is 0b110000000 in binary, so it combines the flags with values 128 (0b10000000) and 256 (0b100000000)



来源:https://stackoverflow.com/questions/55162652/why-exponential-enum-keys-are-used-here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!