一、基础概念
1. 定义与特性
-
唯一性:Symbol 是 ES6 新增的原始数据类型,通过
Symbol()函数创建,每个 Symbol 值都是唯一的。const s1 = Symbol('key'); const s2 = Symbol('key'); s1 === s2; // false -
不可枚举性:默认不会出现在
for...in、Object.keys()中,常用于创建对象私有属性。const obj = { [Symbol('private')]: 'value' }; Object.keys(obj); // []
2. 语法
// 创建 Symbol
const sym = Symbol('description'); // description 仅用于调试,不影响唯一性
// 作为对象属性
const obj = {
[sym]: 'data',
};
二、核心应用场景
1. 作为对象属性键(避免命名冲突)
const NAME = Symbol('name');
class Person {
constructor() {
this[NAME] = '张三';
}
getName() {
return this[NAME];
}
}
2. 定义私有属性/方法
const PRIVATE_METHOD = Symbol();
class MyClass {
[PRIVATE_METHOD]() { /* 私有逻辑 */ }
publicMethod() {
this[PRIVATE_METHOD](); // 内部可访问
}
}
3. 实现自定义迭代器
const obj = {
[Symbol.iterator]: function*() {
yield 1;
yield 2;
}
};
[...obj]; // [1, 2]
4. 模拟枚举类型
const DIRECTION = {
UP: Symbol('up'),
DOWN: Symbol('down'),
};
三、内置 Symbol(元编程)
ES6 提供了一些内置的 Symbol,用于修改对象的默认行为:
| 内置 Symbol | 作用 | 示例 |
|---|---|---|
Symbol.iterator | 定义对象的迭代器方法 | obj[Symbol.iterator] = function() { ... } |
Symbol.toStringTag | 自定义 Object.prototype.toString() 的返回值 | obj[Symbol.toStringTag] = 'MyClass' |
Symbol.toPrimitive | 定义对象转换为原始值的逻辑 | obj[Symbol.toPrimitive] = hint => ... |
Symbol.species | 指定创建衍生对象时使用的构造函数 | class MyArray extends Array { static get [Symbol.species]() { ... } } |
Symbol.hasInstance | 自定义 instanceof 行为 | class MyClass { static [Symbol.hasInstance](obj) { ... } } |
四、问题
1. 问:Symbol 为什么适合作为对象属性?
- **唯一性**:避免属性名冲突(如混入多个第三方库时)。
- **不可枚举性**:默认不会被遍历,适合隐藏实现细节。
2. 问:如何获取对象的 Symbol 属性?
```javascript
const sym = Symbol('key');
const obj = { [sym]: 'value' };
// 方法1:使用 Object.getOwnPropertySymbols()
Object.getOwnPropertySymbols(obj); // [Symbol(key)]
// 方法2:使用 Reflect.ownKeys()(获取所有键,包括 Symbol)
Reflect.ownKeys(obj); // [Symbol(key)]
```
3. 问:Symbol 与 WeakMap 结合实现私有属性的优势?
```javascript
const privateData = new WeakMap();
class Person {
constructor() {
privateData.set(this, { age: 18 });
}
getAge() {
return privateData.get(this).age;
}
}
```
- **优势**:
- 真正私有(外部无法访问 `privateData`)。
- 垃圾回收友好(对象被销毁时,WeakMap 中的引用自动释放)。
4. 问:Symbol 能被序列化吗?
```javascript
const obj = { [Symbol('key')]: 'value' };
JSON.stringify(obj); // {}(Symbol 属性会被忽略)
```
- **原因**:Symbol 是唯一且不可预测的,序列化后无法保证唯一性。