Skip to content

Symbol

Symbol相关特点

  • Symbol 是 ES6 新增了第 7 种原始数据类型 Symbol,。语义为:独一无二的值
  • Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。
  • 每个从 Symbol() 返回的 symbol 值都是唯一的。
  • 它的静态属性会暴露几个内建的成员对象。
  • 它的静态方法会暴露全局的 symbol 注册。

构造函数

  • Symbol() 构造函数返回一个 symbol 类型的值,但是它并不完全支持构造函数的语法new Symbol()
  • 无法被子类化,它可以作为 class 定义中 extends 子句的值使用,但对它进行 super 调用将会导致异常。
javascript
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");
const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");

注意:Symbol("foo") 不会将字符串 "foo" 强制转换成 symbol,而是每次总是创建一个新的 symbol;

静态方法

Symbol.for()

  • 描述:Symbol.for(key) 方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol。如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
javascript
Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol
Symbol.for("foo") = Symbol.for("foo") // true
Symbol("bar") === Symbol("bar"); // false   Symbol() 函数每次都会返回新的一个 symbol
Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol
Symbol.for("foo") = Symbol.for("foo") // true
Symbol("bar") === Symbol("bar"); // false   Symbol() 函数每次都会返回新的一个 symbol

和 Symbol() 不同的是,用 Symbol.for() 方法创建的 symbol 会被放入一个全局 symbol 注册表中。

全局 symbol 注册表中的记录结构:

字段名字段值
[key]一个字符串,用来标识每个 symbol
[symbol]存储的 symbol 值

Symbol.keyFor(sym)

  • 描述:Symbol.keyFor(sym) 方法用来获取全局 symbol 注册表中与某个 symbol 关联的键。
  • 参数:必选参数,需要查找键值的某个 Symbol。
  • 返回:如果全局注册表中查找到该 symbol,则返回该 symbol 的 key 值,返回值为字符串类型。否则返回 undefined
js
// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"
// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"

Symbol的应用

场景一:作为对象属性名(key)

  • Symbol 作为属性名,该属性无法被 for...in、for...of 、Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 这些方法获取。只有Object.getOwnPropertySymbols()方法可以获取指定对象的所有 Symbol 属性名。
javascript
//symbol作为对象属性
let obj_name = Symbol("name");
let obj = {
  [obj_name]: "Bob",
  name: "bob",
  age: 18,
  sex: "man",
};
//symbol作为对象属性
let obj_name = Symbol("name");
let obj = {
  [obj_name]: "Bob",
  name: "bob",
  age: 18,
  sex: "man",
};

场景二:消除魔法字符

javascript
// 代替常量,消除魔法字符。
const TYPE_AUDIO = "AUDIO";
const TYPE_VIDEO = "VIDEO";
const TYPE_IMAGE = "IMAGE";
// Symbol消除与业务代码无关的魔法字符。
const TYPE_AUDIO = Symbol();
const TYPE_VIDEO = Symbol();
const TYPE_IMAGE = Symbol();
// 代替常量,消除魔法字符。
const TYPE_AUDIO = "AUDIO";
const TYPE_VIDEO = "VIDEO";
const TYPE_IMAGE = "IMAGE";
// Symbol消除与业务代码无关的魔法字符。
const TYPE_AUDIO = Symbol();
const TYPE_VIDEO = Symbol();
const TYPE_IMAGE = Symbol();

场景三:定义类的私有属性/方法

javascript
class MyClass {
  // 定义私有属性和方法的 Symbol
  #privateProperty = Symbol('privateProperty');
  #privateMethod = Symbol('privateMethod');

  constructor(value) {
    // 使用 Symbol 定义私有属性
    this[this.#privateProperty] = value;
  }

  // 使用 Symbol 定义私有方法
  [this.#privateMethod]() {
    console.log('This is a private method');
  }

  // 公共方法访问私有属性和方法
  publicMethod() {
    console.log('Private Property:', this[this.#privateProperty]);
    this[this.#privateMethod]();
  }
}

const instance = new MyClass('secret');
instance.publicMethod();
// 输出: Private Property: secret
// 输出: This is a private method

// 无法直接访问私有属性和方法
console.log(instance.privateProperty); // undefined
console.log(instance.privateMethod); // undefined
class MyClass {
  // 定义私有属性和方法的 Symbol
  #privateProperty = Symbol('privateProperty');
  #privateMethod = Symbol('privateMethod');

  constructor(value) {
    // 使用 Symbol 定义私有属性
    this[this.#privateProperty] = value;
  }

  // 使用 Symbol 定义私有方法
  [this.#privateMethod]() {
    console.log('This is a private method');
  }

  // 公共方法访问私有属性和方法
  publicMethod() {
    console.log('Private Property:', this[this.#privateProperty]);
    this[this.#privateMethod]();
  }
}

const instance = new MyClass('secret');
instance.publicMethod();
// 输出: Private Property: secret
// 输出: This is a private method

// 无法直接访问私有属性和方法
console.log(instance.privateProperty); // undefined
console.log(instance.privateMethod); // undefined