VVLL.net

Symbols

日期:2024-08-22 09:58:31

自ECMAScript 2015起,symbol成为了一种新的原生类型,就像number和string一样。

TypeScript 中的 Symbols 是一种独特且不可变的数据类型,每个 Symbol 都是唯一的。它们通常用于定义对象属性的键,并且不会与其他属性键发生冲突。

1. 创建 Symbol

可以使用 Symbol 函数来创建一个新的 Symbol。每个 Symbol 都是唯一的,即使它们的描述相同。

const sym1 = Symbol();
const sym2 = Symbol("description");

console.log(sym1); // Symbol()
console.log(sym2); // Symbol(description)

2. 使用 Symbol 作为对象属性键

由于 Symbol 是唯一的,它们非常适合作为对象属性键,以避免属性名冲突。

const sym = Symbol("key");

const obj = {
  [sym]: "value"
};

console.log(obj[sym]); // "value"

3. 全局 Symbol 注册表

可以使用 Symbol.forSymbol.keyFor 在全局注册表中创建和查找 Symbols。这样即使在不同的模块中也可以共享同一个 Symbol。

const globalSym1 = Symbol.for("globalKey");
const globalSym2 = Symbol.for("globalKey");

console.log(globalSym1 === globalSym2); // true

const key = Symbol.keyFor(globalSym1);
console.log(key); // "globalKey"

4. 内置的 Well-Known Symbols

JavaScript 和 TypeScript 定义了一些内置的 Symbol,这些 Symbol 被称为 Well-Known Symbols,它们可以用来修改内置行为。例如,Symbol.iterator 用于定义对象的默认迭代器。

const myIterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 5) {
          return { value: step, done: false };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

for (const value of myIterable) {
  console.log(value); // 1, 2, 3, 4, 5
}

5. Symbol 属性不可枚举

使用 Object.keysfor...in 循环时,Symbol 属性不会被枚举出来。但是可以使用 Object.getOwnPropertySymbols 来获取对象的 Symbol 属性。

const sym = Symbol("key");
const obj = {
  [sym]: "value",
  regularKey: "regularValue"
};

console.log(Object.keys(obj)); // ["regularKey"]
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(key) ]

6. 使用 Symbol 实现私有属性

尽管 TypeScript 提供了 private 关键字,但在运行时它不会真正将属性隐藏。使用 Symbol 可以实现某种程度的私有属性,因为 Symbol 属性不会被意外访问或覆盖。

const _name = Symbol("name");

class Person {
  constructor(name: string) {
    this[_name] = name;
  }

  getName() {
    return this[_name];
  }
}

const john = new Person("John");
console.log(john.getName()); // John
console.log(john[_name]); // Error: Property '_name' does not exist on type 'Person'

7. 使用 Symbol 实现枚举类型的扩展

Symbol 也可以用于扩展枚举类型,使其更灵活和安全。

enum Directions {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

const Diagonal = {
  [Symbol.for("Up-Left")]: "UP-LEFT",
  [Symbol.for("Up-Right")]: "UP-RIGHT",
  [Symbol.for("Down-Left")]: "DOWN-LEFT",
  [Symbol.for("Down-Right")]: "DOWN-RIGHT"
};

console.log(Directions.Up); // "UP"
console.log(Diagonal[Symbol.for("Up-Left")]); // "UP-LEFT"

8. 示例总结

综合以上使用方式,Symbols 可以在以下场景中大显身手:

  1. 避免对象属性名冲突:使用 Symbol 作为对象属性键,确保属性的唯一性。
  2. 创建私有属性:虽然不是完全的私有,但可以避免意外的访问或覆盖。
  3. 使用全局 Symbol 注册表:在多个模块之间共享同一个 Symbol。
  4. 修改内置行为:通过 Well-Known Symbols 修改内置对象的行为。
  5. 增强枚举类型:使用 Symbol 实现更灵活和安全的枚举类型扩展。

通过合理使用 Symbols,可以大大提升代码的健壮性和可维护性。

参考

标签