Symbol是ES6版本中新增的原始数据类型,和Number、String、Boolean等类型一样,属于JavaScript的基础数据类型范畴。它的核心设计目标是生成独一无二的值,即使两个Symbol的描述内容完全相同,它们本身也不相等,这个特性让它在很多需要避免值冲突的场景下非常实用。

Symbol的基础创建与特性
Symbol不能通过new关键字调用,直接调用Symbol函数就能创建一个Symbol值,函数可以接收一个字符串作为描述信息,这个描述仅用于调试时区分不同的Symbol,不会影响Symbol本身的唯一性。
// 创建两个描述相同的Symbol
const sym1 = Symbol('id');
const sym2 = Symbol('id');
// 即使描述相同,两个Symbol也不相等
console.log(sym1 === sym2); // 输出 false
// 打印Symbol时会带上描述信息,方便调试
console.log(sym1); // 输出 Symbol(id)
console.log(sym2); // 输出 Symbol(id)
Symbol作为属性名时,不会出现在常规的遍历操作中,比如for...in、Object.keys()都无法获取到Symbol属性,只有专门的方法才能读取到这些属性,这个特性让它可以用来定义对象的私有属性或者内部使用的属性。
Symbol的常见应用场景
1. 定义对象的唯一属性名
当给对象添加属性时,如果担心属性名和已有的属性或者后续添加的属性冲突,就可以使用Symbol作为属性名,因为Symbol的唯一性,完全不会出现重名的问题。
const user = {
name: '张三',
age: 20
};
// 用Symbol作为属性名,不会和已有的name、age冲突
const scoreKey = Symbol('score');
user[scoreKey] = 95;
console.log(user.name); // 输出 张三
console.log(user[scoreKey]); // 输出 95
// 常规遍历无法获取到Symbol属性
for (const key in user) {
console.log(key); // 只输出 name、age
}
2. 定义不会冲突的常量
在定义常量时,如果常量是字符串类型,很容易出现值重复的问题,使用Symbol作为常量值就可以完全避免这个问题,每个常量都是独一无二的。
// 定义状态常量,用Symbol避免值冲突
const STATUS = {
PENDING: Symbol('pending'),
SUCCESS: Symbol('success'),
FAIL: Symbol('fail')
};
function handleRequest(status) {
if (status === STATUS.PENDING) {
console.log('请求处理中');
} else if (status === STATUS.SUCCESS) {
console.log('请求成功');
} else if (status === STATUS.FAIL) {
console.log('请求失败');
}
}
handleRequest(STATUS.SUCCESS); // 输出 请求成功
3. 实现对象的迭代器
ES6中对象的迭代功能依赖Symbol.iterator这个内置Symbol,只要对象部署了这个属性,就可以被for...of循环遍历。我们可以自定义对象的迭代逻辑,让普通对象也支持迭代。
const myObj = {
data: [1, 2, 3, 4]
};
// 给对象添加Symbol.iterator属性,实现迭代逻辑
myObj[Symbol.iterator] = function() {
let index = 0;
const self = this;
return {
next() {
if (index < self.data.length) {
return { value: self.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
};
// 现在可以用for...of遍历myObj
for (const item of myObj) {
console.log(item); // 依次输出 1、2、3、4
}
4. 模拟类的私有属性和方法
虽然JavaScript目前没有原生的私有属性语法,但是可以利用Symbol的唯一性和不可遍历性,模拟类的私有成员,外部无法直接访问到这些Symbol定义的成员。
const privateMethod = Symbol('privateMethod');
class MyClass {
constructor() {
this.publicProp = '公开属性';
}
// 用Symbol定义的方法,外部无法直接调用
[privateMethod]() {
console.log('这是私有方法');
}
callPrivate() {
this[privateMethod]();
}
}
const instance = new MyClass();
console.log(instance.publicProp); // 输出 公开属性
instance.callPrivate(); // 输出 这是私有方法
// 外部无法直接调用privateMethod,因为不知道对应的Symbol值
Symbol的注意事项
Symbol值不能和其他类型的值进行运算,比如不能和字符串拼接,也不能和数值做加减操作,否则会抛出错误。另外如果需要获取对象上的所有Symbol属性,可以使用Object.getOwnPropertySymbols()方法,这个方法会返回对象自身的所有Symbol属性数组。
const sym = Symbol('test');
// 以下操作都会报错
// console.log(sym + 'string');
// console.log(sym + 1);
const obj = {
[sym]: 'symbol值'
};
console.log(Object.getOwnPropertySymbols(obj)); // 输出 [Symbol(test)]
除了自定义的Symbol,ES6还内置了一些常用的Symbol值,比如前面用到的Symbol.iterator,还有Symbol.hasInstance、Symbol.toStringTag等,这些内置Symbol可以用来修改对象的一些默认行为,比如修改instanceof的判断逻辑、修改Object.prototype.toString的返回值等,在需要定制对象行为的场景下非常有用。
SymbolES6JavaScriptjs_数据类型修改时间:2026-06-27 15:48:27