大家好,我是 31 岁、爱折腾代码、也爱把技术讲成故事的小米。
那天我在公司翻一段老代码,突然看到一行东西:
我当场愣了三秒。这玩意儿,不像字符串,不像数字,也不像对象,却能左右 for...of、影响数组拼接、甚至决定 instanceof 的结果。
就像一个江湖高手平时不露脸,一出手,全是内功。
今天我们就来讲讲 JavaScript 里这个最“神秘”的基础类型:Symbol。
符号的基本用法:一人一令,绝不撞号
先讲故事。
想象你在一个大型门派里,每个弟子都有一块令牌。名字可以重名,外号可以重复,但令牌编号,绝不可能一样。Symbol 就是这样。
1. Symbol 是什么?
Symbol 是 ES6 新增的一种原始数据类型,它的特点只有一句话:
每一个 Symbol,都是独一无二的。
哪怕你写得一模一样,它们也不是同一个。你还可以给 Symbol 一个“描述”,方便调试:
注意一句非常重要的话:描述只是标签,不是标识。
使用全局符号注册表:江湖通用暗号
有些时候,你不是想“一人一令”,而是想:全江湖,只认这一块令牌。
这时候,就要用 全局符号注册表。
1. Symbol.for
Symbol.for(key) 会做两件事:
- 去全局注册表里找
- 有就返回,没有就创建并登记
2. Symbol.keyFor
反查用的:
注意:
只有通过 Symbol.for 创建的,才在全局表里。
使用符号作为属性:对象里的“暗格抽屉”
现在进入 Symbol 最常见、也最实用的场景。
1. 为什么要用 Symbol 当属性?
因为它有三个特性:
- 不会被意外覆盖
- 不会出现在 for...in
- 非常适合做“内部属性”
就像一个柜子里的暗格。
2. 枚举行为对比
常用内置符号:语言级别的“机关按钮”
如果说普通 Symbol 是你自己造的令牌,那 内置 Symbol,就是 JavaScript 官方留下的“后门接口”。
它们都挂在 Symbol.xxx 上。
Symbol.asyncIterator:异步流水线
这个符号决定一件事:对象能不能被 for await...of 遍历
这是 Node.js、流式处理、异步任务队列的基础。
Symbol.hasInstance:重写 instanceof 规则
instanceof 就像门派认人:“你是不是我门下的?”
Symbol.hasInstance 允许你自定义这套逻辑。
是不是很“不讲武德”?但在框架设计里,非常有用。
Symbol.isConcatSpreadable:数组拼接的开关
默认情况下:
但你可以让数组拒绝展开:
Symbol.iterator:可迭代对象的通行证
这可能是最重要的 Symbol。只要对象实现了它,就能:
- for...of
- 解构
- 展开运算符
Symbol.match:正则的“匹配资格证”
当你写:
JS 实际会去找:
Symbol.replace:自定义 replace 行为
Symbol.search:定义搜索逻辑
Symbol.species:构造器血统问题
它决定:派生对象,用谁来 new?
Symbol.split:拆分规则定制
Symbol.toPrimitive:对象如何变成原始值
这是 JavaScript 类型转换的终极开关。
Symbol.toStringTag:改写 Object.prototype.toString
Symbol.unscopables:with 语句里的“免打扰名单”
这个 Symbol 决定:哪些属性,不应该被 with 拿出来
Symbol 是什么样的人?
如果用一句话形容 Symbol:
它不争曝光,却掌控规则。
它解决了属性冲突
它为语言提供了“协议接口”
它是 JS 内部机制的钥匙
当你开始理解 Symbol,你会发现:
JavaScript 这门语言,真的在认真设计。
END
如果你觉得这篇文章对你理解 Symbol 有帮助,欢迎点赞、收藏、转发。
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!