Symbol
Symbol 是 ES6 引入的一种 基本数据类型,它的主要作用是创建一个独一无二的标识符。即使两个 Symbol 描述相同,它们也是不相等的。
一、Symbol 的特性
-
使用
Symbol()创建,每次调用都会返回一个唯一的值。 -
可以用作对象属性的键,不会被常规的遍历方法枚举出来(比如 for...in、Object.keys)。
-
适合用于定义**“私有属性”或“内部协议”**。
二、基本用法示例
const sym1 = Symbol('description')
const sym2 = Symbol('description')
console.log(sym1 === sym2) // false,唯一性作为对象属性键:
const sym = Symbol('id')
const user = {
name: 'Tom',
[sym]: 123, // 使用 symbol 作为属性名
}
console.log(user[sym]) // 123
console.log(Object.keys(user)) // ['name']
console.log(Object.getOwnPropertySymbols(user)) // [Symbol(id)]注意
symbol使用Symbol定义的对象属性不会出现在普通的遍历中,如for...in、for...of循环或Object.keys()、Object.getOwnPropertyNames()中。它们可以通过Object.getOwnPropertySymbols()获取。
三、常见用途
- 定义对象的私有属性
const secret = Symbol('secret')
class Person {
constructor(name) {
this.name = name
this[secret] = 'mySecret'
}
getSecret() {
return this[secret]
}
}
const p = new Person('Alice')
console.log(p.secret) // undefined
console.log(p.getSecret()) // 'mySecret'- 避免属性名冲突 在扩展第三方对象时可用 Symbol 添加独立属性而不污染已有命名空间。
const tag = Symbol('tag')
const obj = {
[tag]: 'custom',
}- 模拟枚举类型
const COLOR = {
RED: Symbol('red'),
GREEN: Symbol('green'),
}
function getColorName(color) {
switch (color) {
case COLOR.RED:
return 'Red'
case COLOR.GREEN:
return 'Green'
default:
return 'Unknown'
}
}- 内置 Symbol(重要)
ES6 内置了一些 Symbol,用于实现语言底层行为:
-
Symbol.iterator: 实现可迭代协议
-
Symbol.toStringTag: 控制 Object.prototype.toString.call
-
Symbol.hasInstance: 控制 instanceof 行为
-
Symbol.toPrimitive: 控制对象转换为原始值的行为
const obj = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') return 42
return 'default'
},
}
console.log(+obj) // 42
console.log(`${obj}`) // 'default'四、总结
Symbol 是唯一且不可变的标识符,非常适合用于私有属性、避免命名冲突和元编程。
常用于构建更安全、可扩展的模块或库,或增强对象行为的控制。
五、补充
ES6 提供了一组 内置 Symbol(Well-known Symbols),用于定制语言内部行为,也就是让普通对象可以参与 JavaScript 的底层机制。
这些内置 Symbol 都是挂在全局 Symbol 对象上的静态属性,如 Symbol.iterator、Symbol.toPrimitive 等。掌握它们可以让你写出更高级、更底层控制的 JavaScript 代码。
| 内置 Symbol | 作用 | 典型使用场景 |
|---|---|---|
Symbol.iterator | 定义对象的默认迭代器行为 | for...of、... 展开等 |
Symbol.toPrimitive | 定义对象转为原始值的方式 | +obj、${obj} 等 |
Symbol.toStringTag | 控制 Object.prototype.toString.call(obj) 输出 | 自定义类型标签 |
Symbol.hasInstance | 控制 instanceof 的行为 | 自定义 instanceof |
Symbol.isConcatSpreadable | 控制数组/类数组对象是否可被 concat 展开 | 类数组转数组时 |
Symbol.species | 创建派生对象时指定构造函数 | 自定义继承的返回类型 |
Symbol.match / Symbol.replace / Symbol.search / Symbol.split | 控制字符串方法的行为 | 自定义正则-like 对象 |
Symbol.unscopables | 指定 with 语句中不暴露的属性 | 避免冲突 |
Symbol.asyncIterator | 定义异步迭代器 | for await...of 迭代异步数据 |
常用内置 Symbol 详细说明 + 示例
Symbol.iterator:让对象可被迭代
const obj = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0
const self = this
return {
next() {
return index < self.data.length
? { value: self.data[index++], done: false }
: { done: true }
},
}
},
}
for (const val of obj) {
console.log(val) // 1, 2, 3
}Symbol.toPrimitive:对象转原始值时定制行为
const obj = {
name: 'Test',
[Symbol.toPrimitive](hint) {
if (hint === 'number') return 42
if (hint === 'string') return 'symbolic'
return 'default'
},
}
console.log(+obj) // 42
console.log(`${obj}`) // 'symbolic'Symbol.toStringTag:自定义对象类型标签
const obj = {
[Symbol.toStringTag]: 'MyCustomType',
}
console.log(Object.prototype.toString.call(obj)) // [object MyCustomType]Symbol.hasInstance:定制 instanceof
class MyClass {
static [Symbol.hasInstance](instance) {
return 'isSpecial' in instance
}
}
const obj = { isSpecial: true }
console.log(obj instanceof MyClass) // trueSymbol.isConcatSpreadable:控制 concat 展开行为
const arr = [1, 2]
arr[Symbol.isConcatSpreadable] = false
console.log([0].concat(arr)) // [0, [1, 2]]Symbol.species:控制派生对象的构造函数
class MyArray extends Array {
static get [Symbol.species]() {
return Array // 派生对象使用 Array 构造
}
}
const a = new MyArray(1, 2, 3)
const b = a.map((x) => x * 2)
console.log(b instanceof MyArray) // false
console.log(b instanceof Array) // trueSymbol.match/replace/search/split:模拟正则行为
const myMatcher = {
[Symbol.match](str) {
return str.includes('abc') ? ['abc'] : null
},
}
console.log('abcde'.match(myMatcher)) // ['abc']Symbol.asyncIterator:异步可迭代对象(ES2018)
const asyncIterable = {
data: [1, 2, 3],
async *[Symbol.asyncIterator]() {
for (const val of this.data) {
await new Promise((r) => setTimeout(r, 100)) // simulate delay
yield val
}
},
}
;(async () => {
for await (const val of asyncIterable) {
console.log(val) // 1, 2, 3
}
})()Symbol.unscopables:控制with中的变量屏蔽
const obj = {
foo: 1,
bar: 2,
[Symbol.unscopables]: {
bar: true,
},
}
with (obj) {
console.log(foo) // 1
// console.log(bar); // ReferenceError: bar is not defined
}总结
内置 Symbol 是 JS 语言的底层扩展点,能让你控制对象如何参与语言特性(如迭代、类型转换等)。
日常开发中最常用的是:Symbol.iterator、Symbol.toPrimitive、Symbol.toStringTag、Symbol.asyncIterator。
它们广泛用于自定义框架行为、库内部实现、与语言机制对接(比如 polyfill、兼容性库等)。