Symbol

156 阅读3分钟

一、Symbol 是什么?

一句话总结
Symbol 是 ES6 新增的一种原始数据类型(和数字、字符串一样),用来创建独一无二的值,主要用于解决属性名冲突问题。

类比理解
想象你有一堆钥匙,每把钥匙的形状都是独一无二的,即使两把钥匙看起来一样,但它们的“唯一性”能让它们打开不同的锁。Symbol 就像这些独一无二的钥匙!


二、为什么要用 Symbol?

传统问题
如果用字符串作为对象属性名,可能会不小心覆盖已有的属性:

let obj = { name: "小明" };
obj.name = "小红"; // 直接覆盖了原来的 name!

Symbol 的优势
Symbol 作为属性名,能保证属性名永远不会重复,避免意外覆盖!


三、Symbol 的基本用法

1. 创建 Symbol

使用 Symbol() 函数,可以传入一个描述字符串(仅用于调试,不影响唯一性):

// 创建两个不同的 Symbol
const key1 = Symbol("这是一个 Symbol");
const key2 = Symbol("这是一个 Symbol");

console.log(key1 === key2); // false!即使描述相同,值也不同

2. 作为对象属性名

const id = Symbol("id");
const user = {
  name: "小明",
  [id]: 123 // Symbol 作为属性名
};

console.log(user[id]); // 123

3. 属性名的“隐藏性”

  • 普通遍历(如 for...inObject.keys())不会遍历 Symbol 属性:
    const id = Symbol("id");
    const user = { [id]: 123, name: "小明" };
    
    for (let key in user) {
      console.log(key); // 只输出 "name"
    }
    
  • 但可以通过 Object.getOwnPropertySymbols() 获取所有 Symbol 属性:
    const symbols = Object.getOwnPropertySymbols(user); // [Symbol(id)]
    

四、Symbol 的常用方法

1. Symbol.for()

  • 作用:在全局注册表中查找或创建 Symbol。
  • 特点:相同描述的 Symbol 会共享同一个值。
const key1 = Symbol.for("key");
const key2 = Symbol.for("key");
console.log(key1 === key2); // true!

2. Symbol.keyFor()

  • 作用:获取全局 Symbol 的描述。
const globalKey = Symbol.for("globalKey");
console.log(Symbol.keyFor(globalKey)); // "globalKey"

五、内置 Symbol 值

  • JavaScript 预定义了一系列 Symbol 值(如 Symbol.iteratorSymbol.toStringTag),这些值用于标识对象的内置行为
  • 它们的作用是让开发者可以自定义对象在特定操作(如迭代、类型转换)时的逻辑。

1. Symbol.iterator

让对象可以被 for...of 遍历:

const myIterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (let num of myIterable) {
  console.log(num); // 1, 2, 3
}

2. Symbol.toStringTag

自定义对象的 toString() 返回值:

const myObj = {
  [Symbol.toStringTag]: "MyCustomObject"
};

console.log(myObj.toString()); // [object MyCustomObject]

3. 其他

其他所有内置 Symbol(如 Symbol.iteratorSymbol.hasInstance)的基础用法都可以在这查看developer.mozilla.org/zh-CN/docs/…

六、Symbol 的应用场景

1. 避免属性名冲突

// 第三方库的代码
const libraryKey = Symbol("libraryData");
window[libraryKey] = { /* 私有数据 */ };

// 你的代码
const myKey = Symbol("myData");
window[myKey] = { /* 你的数据 */ };
// 双方互不干扰!

2. 模拟私有属性

(虽然 JavaScript 没有真正的私有属性,但可以通过 Symbol 实现类似效果)

const _password = Symbol("password");

class User {
  constructor(name, password) {
    this.name = name;
    this[_password] = password; // “伪私有”属性
  }

  checkPassword(pwd) {
    return this[_password] === pwd;
  }
}

const user = new User("小明", "123456");
console.log(user[_password]); // 可以访问,但需要知道 Symbol 才能操作

3. 定义常量

const LOG_LEVEL = {
  DEBUG: Symbol("debug"),
  INFO: Symbol("info"),
  WARN: Symbol("warn")
};

function log(message, level) {
  if (level === LOG_LEVEL.DEBUG) {
    console.debug(message);
  }
}
// 使用 Symbol 避免常量值重复

七、注意事项

  1. Symbol 不能使用 new

    const s = new Symbol(); // 报错!
    const s = Symbol();     // 正确
    
  2. Symbol 不会被隐式转换

    const s = Symbol();
    console.log("Symbol: " + s); // 报错!不能转字符串
    console.log(String(s));      // 正确:Symbol()
    
  3. JSON.stringify() 会忽略 Symbol

    const obj = { [Symbol("id")]: 123, name: "小明" };
    console.log(JSON.stringify(obj)); // {"name":"小明"}
    

总结

  • Symbol 的核心价值:创建唯一标识符,解决命名冲突。
  • 常用场景:对象属性名、模拟私有属性、定义常量。
  • 记住特点:不可枚举、不可重复、不能隐式转换。