【问题标题】:Why is a Symbol as an index value omitted when iterated?为什么在迭代时省略了作为索引值的符号?
【发布时间】:2021-03-14 22:19:13
【问题描述】:

我确实按照 this idea 在 JavaScript 中使用符号创建了一个 Enum(-like) 结构:

const DATA = Object.freeze({
    GOOD:         Symbol("good"),
    BAD:          Symbol("bad"),
    I_DONT_CARE:  Symbol("i-dont-care"),
});

现在,我想使用(唯一)符号创建一个新对象来引用每个项目并扩展有关它的数据:

const TRANSLATION = Object.freeze({
    [DATA.GOOD]:         "Gut",
    [DATA.BAD]:          "Schlecht",
    [DATA.I_DONT_CARE]:  "Egal",
});

这确实是有效的even though e.g. TypeScript did not believe that and complained,但这也正在修复中。

但是,现在的问题是,如果我使用Object.entries(TRANSLATION)(或Object.keys(TRANSLATION) / Object.values(TRANSLATION))进一步编辑和转换该对象,我会得到一个空对象。

似乎所有Symbols 在迭代时都被忽略了:

myenum = {
  "bla": "blub",
  [Symbol("test")]: "testData"
}
console.log(Object.keys(myenum));

我不希望这样,我只想使用符号作为唯一键,这样我就可以将更多信息存储/“映射”到特定条目。

【问题讨论】:

  • 这些方法就是这样定义的。
  • 这是为了保持一致性。在 ES6 之前,我们有 Object.keys 但没有符号,所以 Object.keys(obj) 在 ES5 和 ES6 中必须以不同方式工作,这使得代码不稳定。 Object.entries 的添加较晚,但是如果迭代符号,它将与 Object.keys 不一致。 Object.getOwnPropertySymbols 仅提取符号或 Object.getOwnPropertyDescriptors 提取所有内容。
  • 另外,还有Reflect.ownKeys(我一直忘记Reflect,因为我不经常使用它)。

标签: javascript ecmascript-6 enums iteration


【解决方案1】:

符号在 for...in 迭代中是不可枚举的。此外,Object.getOwnPropertyNames() 不会返回符号对象属性,但是,您可以使用 Object.getOwnPropertySymbols() 来获取这些属性。 - MDN

这就是符号的工作方式。它们不会被迭代。所以使用Object.keys() 会将它们从结果中排除。

但是,您可以使用 Object.getOwnPropertySymbols() 从对象中获取符号,然后将它们与 Object.keys() 组合以创建所有键的最终列表。

检查下面的示例以查看它是否有效。请注意,堆栈 sn-p 控制台将 Symbol 解释为 null。但是,当您打开浏览器的控制台时,您应该会看到实际的结果。

const myenum = {
  "bla": "blub",
  [Symbol("foo")]: "bar",
  [Symbol("roger")]: "roger"
}

const iterableKeys = Object.keys(myenum);
const symbolKeys = Object.getOwnPropertySymbols(myenum);
const keys = [...iterableKeys, ...symbolKeys];

console.log(keys);

const values = keys.map(key => myenum[key]);

console.log(values);

作为 VLAZ 建议使用 Reflect.ownKeys 的更短的替代方案

它的返回值等价于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。

这意味着它已经默认结合了Object.keys()(但使用Object.getOwnPropertyNames)和Object.getOwnPropertySymbols()

const myenum = {
  "bla": "blub",
  [Symbol("foo")]: "bar",
  [Symbol("roger")]: "roger"
}

const keys = Reflect.ownKeys(myenum);

console.log(keys);

const values = keys.map(key => myenum[key]);

console.log(values);

【讨论】:

    【解决方案2】:

    符号不会被迭代。从我的角度来看,它们对于隐藏属性和生成独特的重用值更有用。

    我建议使用 TypeScript 实现,我认为它已经过迭代和实战测试。

    TypeScript 支持枚举:

    enum Direction {
      Up = 1,
      Down,
      Left,
      Right,
    }
    

    下面的JS得到transpiled

    var Direction;
    (function (Direction) {
        Direction[Direction["Up"] = 1] = "Up";
        Direction[Direction["Down"] = 2] = "Down";
        Direction[Direction["Left"] = 3] = "Left";
        Direction[Direction["Right"] = 4] = "Right";
    })(Direction || (Direction = {}));
    
    // resulting object 
    // Direction = {
    //   1: "Up",
    //   2: "Down",
    //   3: "Left",
    //   4: "Right",
    //   "Up": 1,
    //   "Down": 2,
    //   "Left": 3,
    //   "Right": 4
    // }
    

    如果您从符号切换到唯一字符串,问题将得到解决。

    【讨论】:

      【解决方案3】:

      啊,好吧,原因是:符号不是enumerable。幸运的是,有 Object.getOwnPropertySymbols() 至少返回一个包含所有符号的数组,类似于Object.keys

      有了它,您当然还可以以老式的 pre-ECMAScript 2017 方式“模拟”更高级的功能,例如 OBject.values

      const myenum = {
        "bla": "blub",
        [Symbol("test")]: "testData",
        [Symbol("goodsymbol")]: "baddata"
      }
      console.log(Object.getOwnPropertySymbols(myenum).map((mysymbol) => {
        const value = myenum[mysymbol];
        return [mysymbol, value];
      }));

      【讨论】:

      猜你喜欢
      • 2016-02-25
      • 2017-02-12
      • 2018-09-29
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 2016-10-06
      • 2020-03-07
      相关资源
      最近更新 更多