为啥在迭代时省略了作为索引值的符号?
Posted
技术标签:
【中文标题】为啥在迭代时省略了作为索引值的符号?【英文标题】: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
,因为我不经常使用它)。
【参考方案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];
));
【讨论】:
更正符号是可枚举的。但是,它们不会被迭代。 The enumerable property is still taken into account in different cases. 另外,如果你想遍历所有键,我建议Reflect.ownKeys(myenum)
。它也恰好更短。以上是关于为啥在迭代时省略了作为索引值的符号?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Dart (Flutter) 中获取每个可迭代值的索引号