• enum is a new LexicalDeclaration binding form, similar to let or const.

  • typeof enum is object; similar to Array, it's just a special Object (more below)

  • EnumDeclaration with no BindingIdentifier creates const bindings corresponding to each EnumEntryName:

    enum {
      SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
    }

    Is equivalent to:

    const SUNDAY = 0;
    const MONDAY = 1;
    const TUESDAY = 2;
    const WEDNESDAY = 3;
    const THURSDAY = 4;
    const FRIDAY = 5;
    const SATURDAY = 6;
  • EnumDeclaration or EnumExpression with a BindingIdentifier creates:

    • A proto-less, frozen object;

    • Creates PropertyName corresponding to each EnumEntryName:

      enum DaysOfWeek {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
      }
      
      const DaysOfWeek = enum {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY    
      };

      Is approximately* equivalent to:

      const DaysOfWeek = Object.freeze(
        Object.create(null, {
          [Symbol.enumSize]: {
            // Specification can define better semantics for deriving 
            // and storing the size of the enum object (internal slot)
            value: 7
          }, 
          [Symbol.iterator]: {
            * value() {
              // Specification can define better semantics for deriving 
              // and storing keys and values (internal slot)
              let keys = Object.keys(DaysOfWeek); 
              let values = Object.values(DaysOfWeek);
              let index = 0;
              while (index < keys.length) {
                yield [keys[index], values[index]];
                index++;
              }
            }
          },
          SUNDAY: { value: 0, enumerable: true },
          MONDAY: { value: 1, enumerable: true },
          TUESDAY: { value: 2, enumerable: true },
          WEDNESDAY: { value: 3, enumerable: true },
          THURSDAY: { value: 4, enumerable: true },
          FRIDAY: { value: 5, enumerable: true },
          SATURDAY: { value: 6, enumerable: true },
        })
      );
      
      for (let [day, value] of DaysOfWeek) { console.log(value, day) }
      
      /*
        0 SUNDAY
        1 MONDAY
        2 TUESDAY
        3 WEDNESDAY
        4 THURSDAY
        5 FRIDAY
        6 SATURDAY    
       */
  • EnumDeclaration or EnumExpression will set the value of each entry to an integer, starting at 0 and incrementing by 1 for each entry.

  • EnumDeclaration may have EnumEntryName inline assignment overrides. If an entry has an inline assignment, the default value is incremented as normal, but not used for that entry.

    enum {
      FOO, 
      BAR = _AssignmentExpression_, 
      BAZ, 
    }

    Is equivalent to:

    const FOO = 0;
    const BAR = _AssignmentExpression_;
    const BAZ = 2;

    EnumDeclaration (with BindingIdentifier) or EnumExpression example:

    enum Things {
      FOO, 
      BAR = _AssignmentExpression_, 
      BAZ, 
    }
    
    const Things = enum {
      FOO, 
      BAR = _AssignmentExpression_, 
      BAZ, 
    };

    Is approximately equivalent to:

    const DaysOfWeek = Object.freeze(
      Object.create(null, {
        /*
        Assume Symbol.enumSize and Symbol.iterator
         */
        FOO: { value: 0, enumerable: true },
        BAR: { value: _AssignmentExpression_, enumerable: true },
        BAZ: { value: 2, enumerable: true },
      })
    );

    Of course, this means that the value of an enum entry can be whatever you want it to be:

    // todo
  • EnumDeclaration may have ComputedPropertyName as EnumEntryName:

    • TODO: Am I sure about this?
    enum {
      FOO, 
      ["BAR"], 
      BAZ, 
    }

    Is equivalent to:

    const FOO = 0;
    const BAR = 1;
    const BAZ = 2;

    EnumDeclaration (with BindingIdentifier) or EnumExpression example:

    enum Things {
      FOO, 
      [Symbol(...)], 
      BAZ, 
    }
    
    const Things = enum {
      FOO, 
      [Symbol(...)], 
      BAZ, 
    };

    Is approximately equivalent to:

    const DaysOfWeek = Object.freeze(
      Object.create(null, {
        /*
        Assume Symbol.enumSize and Symbol.iterator
         */
        FOO: { value: 0, enumerable: true },
        [Symbol(...)]: { value: 1, enumerable: true },
        BAZ: { value: 2, enumerable: true },
      })
    );
  • EnumDeclaration may not have duplicate EnumEntryName:

    • These throw exceptions:

      enum {
        FOO, 
        FOO
      }
      enum {
        FOO, 
        ["BAR"], 
        BAR, 
      }
  • EnumDeclaration with an EnumValueMap to populate the values of the Enum:

    function EnumValueMap(key, index) {
      return 1 << index;
    }
    
    enum via EnumValueMap {
      SUNDAY,
      MONDAY,
      TUESDAY,
      WEDNESDAY,
      THURSDAY,
      FRIDAY,
      SATURDAY,
    }
    
    // Is equivalent to: 
    
    const SUNDAY    = EnumValueMap(SV(SUNDAY), 0);   // 0b00000001
    const MONDAY    = EnumValueMap(SV(MONDAY), 1);   // 0b00000010
    const TUESDAY   = EnumValueMap(SV(TUESDAY), 2);  // 0b00000100
    const WEDNESDAY = EnumValueMap(SV(WEDNESDAY), 3);// 0b00001000
    const THURSDAY  = EnumValueMap(SV(THURSDAY), 4); // 0b00010000
    const FRIDAY    = EnumValueMap(SV(FRIDAY), 5);   // 0b00100000
    const SATURDAY  = EnumValueMap(SV(SATURDAY), 6); // 0b01000000

    And the BindingIdentifier example:

    function EnumValueMap(key, index) {
      return 1 << index;
    }
    
    enum DaysOfWeekAsBits via EnumValueMap {
      SUNDAY,
      MONDAY,
      TUESDAY,
      WEDNESDAY,
      THURSDAY,
      FRIDAY,
      SATURDAY,
    }

    Is approximately equivalent to:

    const DaysOfWeekAsBits = Object.freeze(
      Object.create(null, {
        [Symbol.enumSize]: {
          // Specification can define better semantics for deriving 
          // and storing the size of the enum object (internal slot)
          value: 7
        }, 
        [Symbol.iterator]: {
          * value() {
            // Specification can define better semantics for deriving 
            // and storing keys and values (internal slot)
            let keys = Object.keys(DaysOfWeekAsBits); 
            let values = Object.values(DaysOfWeekAsBits);
            let index = 0;
            while (index < keys.length) {
              yield [keys[index], values[index]];
              index++;
            }
          }
        },
        SUNDAY: { value: EnumValueMap(SV(SUNDAY), 0), enumerable: true },
        MONDAY: { value: EnumValueMap(SV(MONDAY), 1), enumerable: true },
        TUESDAY: { value: EnumValueMap(SV(TUESDAY), 2), enumerable: true },
        WEDNESDAY: { value: EnumValueMap(SV(WEDNESDAY), 3), enumerable: true },
        THURSDAY: { value: EnumValueMap(SV(THURSDAY), 4), enumerable: true },
        FRIDAY: { value: EnumValueMap(SV(FRIDAY), 5), enumerable: true },
        SATURDAY: { value: EnumValueMap(SV(SATURDAY), 6), enumerable: true },
      })
    );
    
    for (let [day, value] of DaysOfWeekAsBits) { console.log(value, day) }
    
    /*
      0b00000001 SUNDAY
      0b00000010 MONDAY
      0b00000100 TUESDAY
      0b00001000 WEDNESDAY
      0b00010000 THURSDAY
      0b00100000 FRIDAY
      0b01000000 SATURDAY    
     */

    WebIDL could specify its enum as using a default EnumValueMap; ie. https://heycam.github.io/webidl/#idl-enumeration might update its definition:

    An enumeration is a definition (matching Enum) used to declare a type whose valid values are a set of predefined strings.

    To:

    An enumeration is a definition (matching Enum) used to declare a type whose valid values are a set of predefined strings.

    An ECMAScript implementation of WebIDL enum would use a String EnumValueMap.

    enum RTCPeerConnectionState via String { 
      new,
      connecting,
      connected,
      disconnected,
      failed,
      closed,
    };

    Is approximately equivalent to:

    const RTCPeerConnectionState =Object.freeze(
      Object.create(null, {
        [Symbol.enumSize]: {
          // Specification can define better semantics for deriving 
          // and storing the size of the enum object (internal slot)
          value: 6
        }, 
        [Symbol.iterator]: {
          * value() {
            // Specification can define better semantics for deriving 
            // and storing keys and values (internal slot)
            let keys = Object.keys(RTCPeerConnectionState); 
            let values = Object.values(RTCPeerConnectionState);
            let index = 0;
            while (index < keys.length) {
              yield [keys[index], values[index]];
              index++;
            }
          }
        },
        // Note that this case illustrates that it's ok for the 
        // for the second argument to be ignored.
        new: { value: String(SV(new)), enumerable: true },
        connecting: { value: String(SV(connecting)), enumerable: true },
        connected: { value: String(SV(connected)), enumerable: true },
        disconnected: { value: String(SV(disconnected)), enumerable: true },
        failed: { value: String(SV(failed)), enumerable: true },
        closed: { value: String(SV(closed)), enumerable: true },
      })
    );

* Approximately means that it doesn't fully represent all of the semantics.