symbol属性在json中彻底隐身,因json标准仅支持字符串键且symbol不可枚举,故json.stringify()根本不会扫描到它,连丢弃动作都不存在。
直接说结论:用 symbol 做对象属性名,不是为了“隐藏敏感数据”,而是让这些属性天然不进 json.stringify() —— 它们压根不会被扫描到,连“丢弃”都算不上,是彻底隐身。
为什么 Symbol 属性在 JSON 里自动消失
JSON 标准(RFC 8259)只认七种类型,且明确规定:对象的键必须是字符串。Symbol 是 JavaScript 独有的原始类型,既不是字符串,也不可枚举(默认),所以 JSON.stringify() 的内部遍历逻辑从一开始就会跳过所有 Symbol 键和 Symbol 值:
- Symbol 作键:整个键值对完全不出现,结果中毫无痕迹
- Symbol 作值(如 { x: Symbol('token') }):该属性被整条忽略,不报错、不警告、不占位
- 即使你传了 replacer 函数,它也收不到 Symbol 相关的任何调用——因为遍历阶段根本没看到它们
正确写法:用 const 定义一次,反复复用
Symbol 值每次调用 Symbol() 都生成新值,哪怕描述一样也不相等。所以必须把 Symbol 存成常量,否则无法稳定读写:
- ✅ 正确:const TOKEN = Symbol('auth-token'); obj[TOKEN] = 'abc123'; console.log(obj[TOKEN]);
- ❌ 错误:console.log(obj[Symbol('auth-token')]); // undefined —— 新建的 Symbol 和原来的不等
- 不要在 JSON 序列化前动态拼 Symbol 键,比如 obj[Symbol(key)],除非 key 是固定字符串常量
它防的是什么,不防什么
Symbol 不是安全边界,而是一种“约定级防护”:
- ✅ 防意外暴露:Object.keys()、for...in、JSON.stringify()、Object.assign()、展开运算符 {...obj} 全部看不到它
- ✅ 防命名冲突:第三方库用 '_token' 或 'token' 不会覆盖你的 TOKEN
- ❌ 不防主动访问:只要别人拿到你的 TOKEN 变量(比如模块导出、调试时看到),obj[TOKEN] 依然可读可写
- ❌ 不防反射探测:Object.getOwnPropertySymbols(obj) 或 Reflect.ownKeys(obj) 仍能列出它
比 Symbol 更强的替代方案(按需选用)
如果目标是真正隔离敏感数据,尤其是不想让任何代码(包括同模块其他函数)随意触碰,推荐组合使用:
- WeakMap + Symbol:把 Symbol 当作 WeakMap 实例的标识名,实际数据存在 WeakMap 里。外部拿不到 WeakMap 引用就完全无法访问
- 私有字段 #token:语法级限制,this.#token 外部访问直接报错,比 Symbol 更严格(但不兼容 IE 和极老 Node.js)
- 闭包封装:构造函数内用 let token 声明,仅通过方法暴露有限接口,彻底隔绝属性访问路径











