历史背景:
Symbol 类型是在 JavaScript(ES6)中添加的,用于创建唯一标识符,确保其与对象的其他属性不会冲突。TypeScript 自 ES6 兼容性起支持符号。
问题:
在 Symbol 出现之前,字符串经常被用作对象属性的键。这导致在扩展或重用对象时出现错误:随机名称冲突以及无法隐藏私有属性(即使通过约定)。Symbol 允许创建唯一且对外部代码不可见的键,但在类型化方面出现了问题——如何描述 Symbol 键的类型,以及如何在 API 中安全使用它们?
解决方案:
TypeScript 支持将符号作为值和类型,然而 Symbol 键的类型化具有特殊性。可以使用全局构造函数或全局符号注册表来创建符号。在接口或类型中,带有符号的键必须明确指定类型为 symbol,而对这些属性的访问只能通过保存的 Symbol 引用进行。
代码示例:
const SECRET = Symbol('secret'); interface SecretObject { [SECRET]: string; visible: string; } const obj: SecretObject = { visible: 'see me', [SECRET]: 'hidden', }; console.log(obj.visible); // 'see me' // console.log(obj["secret"]); // 错误:没有这样的属性! console.log(obj[SECRET]); // 'hidden'
关键特点:
Symbol 在对象中使用时能自动转换为字符串吗?
不,Symbol 不能自动转换为字符串,尝试进行此操作(例如,通过连接)将导致错误。
const mySymbol = Symbol('desc'); // alert('prefix_' + mySymbol); // TypeError
可以通过 Object.keys 列出 Symbol 键吗?
不,Object.keys 和 for...in 会忽略 Symbol 键。获取这些键需要使用 Object.getOwnPropertySymbols。
const sym = Symbol('a'); const obj = { [sym]: 42 }; Object.keys(obj); // [] Object.getOwnPropertySymbols(obj); // [Symbol(a)]
通过 Object.assign 复制时会传递带有 Symbol 键的属性吗?
是的,Object.assign 会同时复制字符串键和符号键,与 JSON.stringify 不同。
const s = Symbol('s'); const o1 = { [s]: 123, foo: 'bar' }; const o2 = Object.assign({}, o1); o2[s]; // 123
开发者使用字符串键('_private')作为私有属性,依靠约定。在 B 组意外添加了相同字符串 — 属性发生了覆盖,出现了不可预测的错误。
优点:
缺点:
第二位开发者使用 Symbol (例如,Symbol('internal'))作为隐藏属性。现在即使在团队内部,也无法意外地覆盖内部数据:只有在拥有特定 Symbol 的引用时才能访问。
优点:
缺点: