Symbol 是 ES6 引入的一种新的原始数据类型,它表示一个唯一的标识符。 每个 Symbol 值都是唯一的,即使是用相同的描述创建的 Symbol 也不相等。
const sym1 = Symbol();const sym2 = Symbol();console.log(sym1 === sym2); // false,每个Symbol都是唯一的
const sym3 = Symbol("description");const sym4 = Symbol("description");console.log(sym3 === sym4); // false,即使描述相同也不相等在 ES6 之前,对象的属性名只能是字符串。这会导致属性名冲突的问题,特别是在:
// 传统方式可能出现的问题const person = { name: "Kayn" };person.name = "Alice"; // 意外覆盖了原有属性
// 使用Symbol避免冲突const nameSymbol = Symbol("name");person[nameSymbol] = "Bob"; // 不会影响原有的name属性console.log(person.name); // "Alice"console.log(person[nameSymbol]); // "Bob"// 创建一个Symbolconst sym = Symbol();
// 创建带描述的Symbol(描述仅用于调试)const namedSym = Symbol("mySymbol");console.log(namedSym.toString()); // "Symbol(mySymbol)"Symbol.for() 会在全局 Symbol 注册表中查找,如果存在相同 key 的 Symbol 就返回它,否则创建新的
const globalSym1 = Symbol.for("app.id");const globalSym2 = Symbol.for("app.id");console.log(globalSym1 === globalSym2); // true,它们是同一个Symbol
// 获取Symbol的keyconsole.log(Symbol.keyFor(globalSym1)); // "app.id"
// 普通Symbol没有keyconst localSym = Symbol("local");console.log(Symbol.keyFor(localSym)); // undefined每个 Symbol 都是独一无二的
const sym1 = Symbol("test");const sym2 = Symbol("test");const sym3 = Symbol.for("test");const sym4 = Symbol.for("test");
console.log(sym1 === sym2); // false,普通Symbol总是唯一的console.log(sym3 === sym4); // true,全局Symbol相同key返回同一个Symbol 一旦创建就不能修改
const sym = Symbol("original");// 没有方法可以修改Symbol的值或描述const sym = Symbol("test");console.log(String(sym)); // "Symbol(test)",需要显式转换console.log(sym.toString()); // "Symbol(test)"
// 这些会报错// console.log(sym + ""); // TypeError// console.log(+sym); // TypeErrorconst sym = Symbol("property");const obj = {};
// 三种设置Symbol属性的方式obj[sym] = "value1";obj[Symbol("key2")] = "value2";
const obj2 = { [Symbol("key3")]: "value3",};Symbol 属性不会出现在常规的对象遍历中
const visibleProp = "visible";const hiddenProp = Symbol("hidden");
const obj = { [visibleProp]: "可以看到", [hiddenProp]: "隐藏属性",};
// 常规遍历看不到Symbol属性for (let key in obj) { console.log(key); // 只输出 "visible"}
console.log(Object.keys(obj)); // ["visible"]console.log(Object.getOwnPropertyNames(obj)); // ["visible"]
// 需要专门的方法获取Symbol属性console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(hidden)]console.log(Reflect.ownKeys(obj)); // ["visible", Symbol(hidden)]JavaScript 提供了许多内置 Symbol,用于元编程和自定义对象行为:
定义对象的默认迭代器
const iterable = { data: [1, 2, 3], [Symbol.iterator]() { let index = 0; const data = this.data; return { next() { if (index < data.length) { return { value: data[index++], done: false }; } return { done: true }; }, }; },};
for (let item of iterable) { console.log(item); // 1, 2, 3}自定义对象的字符串描述
class CustomClass { get [Symbol.toStringTag]() { return "CustomClass"; }}
const obj = new CustomClass();console.log(obj.toString()); // "[object CustomClass]"自定义 instanceof 行为
class MyArray { static [Symbol.hasInstance](instance) { return Array.isArray(instance); }}
console.log([] instanceof MyArray); // trueconsole.log({} instanceof MyArray); // false// 库代码const LIBRARY_ID = Symbol("libraryId");
class LibraryClass { constructor() { this[LIBRARY_ID] = Math.random(); }
getLibraryId() { return this[LIBRARY_ID]; }}
// 用户代码不会意外覆盖库的内部属性const instance = new LibraryClass();instance.libraryId = "user value"; // 不会影响库的内部状态const Colors = { RED: Symbol("red"), GREEN: Symbol("green"), BLUE: Symbol("blue"),};
function processColor(color) { switch (color) { case Colors.RED: return "处理红色"; case Colors.GREEN: return "处理绿色"; case Colors.BLUE: return "处理蓝色"; default: return "未知颜色"; }}
console.log(processColor(Colors.RED)); // "处理红色"const _private = Symbol("private");
class User { constructor(name, password) { this.name = name; this[_private] = { password }; // "私有"属性 }
checkPassword(pwd) { return this[_private].password === pwd; }}
const user = new User("Kayn", "secret");console.log(user.name); // "Kayn"console.log(user.password); // undefined,无法直接访问console.log(user.checkPassword("secret")); // trueSymbol 属性不会被 JSON.stringify() 包含
const sym = Symbol("key");const obj = { name: "Kayn", [sym]: "hidden value",};
console.log(JSON.stringify(obj)); // '{"name":"Kayn"}',Symbol属性丢失总是为 Symbol 提供有意义的描述
// ✅ 好的做法const userIdSymbol = Symbol("userId");const configSymbol = Symbol("config");
// ❌ 不好的做法const sym1 = Symbol();const sym2 = Symbol();虽然 Symbol 查找很快,但创建 Symbol 有一定开销
// ✅ 在模块顶层创建,复用Symbolconst SHARED_SYMBOL = Symbol("shared");
function processData(data) { return data[SHARED_SYMBOL];}
// ❌ 避免在循环中创建Symbolfunction badExample(arr) { arr.forEach((item) => { const sym = Symbol("temp"); // 每次都创建新的Symbol item[sym] = "value"; });}| 特性 | 描述 |
|---|---|
| 唯一性 | 每个Symbol都是独一无二的 |
| 不可变 | 创建后无法修改 |
| 不可枚举 | 不出现在for…in、Object.keys()等中 |
| 不自动转换 | 不参与隐式类型转换 |
| 全局注册 | Symbol.for()可创建全局共享的Symbol |
Symbol 是现代 JavaScript 中一个强大的特性,主要用于创建唯一标识符、避免属性名冲突,以及实现元编程。正确使用 Symbol 可以让代码更加健壮和安全。