**Symbol **
Symbol 是 ES6 引入的一种新的原始数据类型,表示唯一、不可变的值。它是 JavaScript 的第7种数据类型(前6种是:undefined、null、布尔值、字符串、数值、对象)。
1. 基本概念
创建 Symbol
// 创建 Symbol
const sym1 = Symbol();
const sym2 = Symbol();
console.log(sym1 === sym2); // false,每个Symbol都是唯一的
console.log(typeof sym1); // "symbol"
带描述的 Symbol
// 创建带描述的 Symbol(用于调试)
const sym = Symbol('这是一个描述');
console.log(sym.toString()); // "Symbol(这是一个描述)"
console.log(sym.description); // "这是一个描述"(ES2019新增)
Symbol 不是构造函数
// ❌ 错误:不能使用 new
const sym = new Symbol(); // TypeError: Symbol is not a constructor
// ✅ 正确:直接调用
const sym = Symbol();
2. 核心特性
唯一性
// 相同描述,不同值
const sym1 = Symbol('foo');
const sym2 = Symbol('foo');
console.log(sym1 === sym2); // false
console.log(sym1 === Symbol('foo')); // false
不可变
const sym = Symbol('original');
// Symbol 值创建后无法修改
不会隐式转换
const sym = Symbol('test');
// ❌ 不能转为字符串(隐式转换)
console.log('Symbol: ' + sym); // TypeError
// ✅ 可以显式转换
console.log(String(sym)); // "Symbol(test)"
console.log(sym.toString()); // "Symbol(test)"
// ✅ 可以转为布尔值
console.log(Boolean(sym)); // true
console.log(!sym); // false
// ❌ 不能转为数字
console.log(Number(sym)); // TypeError
3. 主要用途
用途1:作为对象属性键
// 防止属性名冲突
const obj = {};
const NAME_KEY = Symbol('name');
obj[NAME_KEY] = '小明';
obj.name = '小红'; // 不会冲突
console.log(obj[NAME_KEY]); // "小明"
console.log(obj.name); // "小红"
// 在对象字面量中使用
const obj2 = {
[Symbol('id')]: 123, // 使用计算属性名
name: '小明'
};
用途2:模拟私有属性
// 在类中模拟私有成员
const _privateData = Symbol('privateData');
class MyClass {
constructor() {
this[_privateData] = '秘密数据';
this.publicData = '公开数据';
}
getPrivateData() {
return this[_privateData];
}
}
const instance = new MyClass();
console.log(instance.publicData); // "公开数据"
console.log(instance.getPrivateData()); // "秘密数据"
// 但可以通过 Object.getOwnPropertySymbols 获取
const symbols = Object.getOwnPropertySymbols(instance);
console.log(instance[symbols[0]]); // "秘密数据" - 非真正私有
用途3:定义常量
// 替代魔法字符串/数字
const LogLevel = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
ERROR: Symbol('error')
};
function log(message, level) {
switch(level) {
case LogLevel.DEBUG:
console.debug(message);
break;
case LogLevel.ERROR:
console.error(message);
break;
// ...
}
}
log('测试', LogLevel.DEBUG);
** 面试常见问题**
Q1: Symbol 是什么?有什么用?
A: Symbol 是 ES6 新增的原始数据类型,表示唯一值。主要用于:
- 防止对象属性名冲突
- 模拟私有属性
- 定义内置方法(如
Symbol.iterator) - 实现常量枚举
Q2: Symbol.for() 和 Symbol() 的区别?
A:
Symbol()每次返回新的唯一值Symbol.for()在全局注册表中查找,存在则返回,不存在则创建并注册
Q3: 如何遍历 Symbol 属性?
A: 使用 Object.getOwnPropertySymbols()或 Reflect.ownKeys()
Q4: Symbol 是真正的私有吗?
A: 不是,可以通过 Object.getOwnPropertySymbols()获取。真正私有用 WeakMap或私有字段 #
Q5: Symbol 的主要应用场景?
A:
- 库/框架开发避免属性冲突
- 定义常量枚举
- 实现可迭代对象
- 自定义对象的内置行为
总结
| 特性 | 说明 |
|---|---|
| 类型 | 第7种原始数据类型 |
| 唯一性 | 每个 Symbol 值都是唯一的 |
| 不可变 | 创建后不可修改 |
| 不可转换 | 不能隐式转换为字符串/数字 |
| 作为属性键 | 不会出现在常规遍历中 |
| 全局注册 | 通过 Symbol.for()共享 |
| 内置 Symbol | 11个,用于改变对象的内置行为 |
| 实际用途 | 防冲突、模拟私有、元编程、常量定义 |
最佳使用场景:
- 需要唯一标识符时:使用
Symbol() - 需要全局共享标识符时:使用
Symbol.for() - 避免属性名冲突时:使用 Symbol 作为键
- 定义常量时:比字符串/数字更安全
- 自定义对象行为时:使用内置 Symbol 值
注意事项:Symbol 不是真正的私有机制,如果需要真正私有,考虑使用 WeakMap或 ES2022 的私有字段 #。