object.entries 无法获取 symbol 键名,因其仅返回字符串键的可枚举自有属性;需手动合并 object.keys() 与过滤后的 object.getownpropertysymbols() 结果才能完整序列化。

Object.entries 本身无法获取 Symbol 键名的属性,它只返回字符串键名的可枚举自有属性。要实现“包含 Symbol 键名”的业务对象完整序列化,必须手动合并字符串键和 Symbol 键两部分数据。
为什么 Object.entries 不够用
Object.entries(obj) 等价于:
Object.keys(obj).map(key => [key, obj[key]])
它完全忽略 Symbol 类型的键名,哪怕该 Symbol 是对象的自有、可枚举属性(例如通过 Object.defineProperty(obj, sym, { value: 42, enumerable: true }) 添加)。
获取所有自有可枚举键(含 Symbol)的正确方式
需组合使用:
- Object.keys(obj) → 字符串键
- Object.getOwnPropertySymbols(obj) → Symbol 键(包括不可枚举的,需过滤)
再统一筛选出可枚举项,构造成 entries 格式:
- 遍历
Object.keys(obj),保留所有项(字符串键的 enumerable 默认由 defineProperty 控制,但 Object.keys 本身已只返回可枚举字符串键) - 遍历
Object.getOwnPropertySymbols(obj),对每个 symbol 用Object.getOwnPropertyDescriptor(obj, sym)?.enumerable === true判断是否可枚举 - 合并两组结果,按需排序(如保持定义顺序:先字符串键后 Symbol 键,或统一按描述符中的定义顺序)
封装一个兼容 Symbol 的 entries 函数
示例实现(支持 Symbol + 可枚举性判断):
function allEnumerableEntries(obj) {
if (obj == null || typeof obj !== 'object') return [];
<p>const strKeys = Object.keys(obj);
const symKeys = Object.getOwnPropertySymbols(obj)
.filter(sym => Object.getOwnPropertyDescriptor(obj, sym)?.enumerable);</p><p>return [
...strKeys.map(key => [key, obj[key]]),
...symKeys.map(sym => [sym, obj[sym]])
];
}</p>
这样得到的数组结构与 Object.entries 一致,每一项是 [key, value],其中 key 可能是 string 或 Symbol,后续可直接用于 JSON.stringify(需额外处理 Symbol)或自定义序列化逻辑。
序列化时如何处理 Symbol 键名
JSON 标准不支持 Symbol,直接 JSON.stringify(allEnumerableEntries(obj)) 会导致 Symbol 键变成 null 或被忽略。实际业务中常见策略有:
-
转为描述性字符串:如
sym.toString()→"Symbol(description)",再存入 JSON;反序列化时按约定重建 Symbol -
跳过 Symbol 键:仅序列化字符串键(适合 Symbol 仅作内部标记,如
obj[Symbol.for('cache')] = new Map()) - 分离存储:将 Symbol 键值对单独提取,用自定义格式(如 MessagePack、Protocol Buffers)或附加元字段保存
-
业务约定 key 命名:避免用 Symbol 存业务数据,改用字符串前缀(如
"__sym_cache"),兼顾可读性与序列化友好性










