ZDecode
Javascript

Symbol

Symbol 是 ES6 引入的一种 基本数据类型,它的主要作用是创建一个独一无二的标识符。即使两个 Symbol 描述相同,它们也是不相等的

一、Symbol 的特性

  • 使用 Symbol() 创建,每次调用都会返回一个唯一的值。

  • 可以用作对象属性的键,不会被常规的遍历方法枚举出来(比如 for...in、Object.keys)。

  • 适合用于定义**“私有属性”“内部协议”**。

二、基本用法示例

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

console.log(sym1 === sym2); // false,唯一性

作为对象属性键:

const sym = Symbol('id');
const user = {
  name: 'Tom',
  [sym]: 123 // 使用 symbol 作为属性名
};

console.log(user[sym]); // 123
console.log(Object.keys(user)); // ['name']
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]

三、常见用途

  1. 定义对象的私有属性
const secret = Symbol('secret');

class Person {
  constructor(name) {
    this.name = name;
    this[secret] = 'mySecret';
  }

  getSecret() {
    return this[secret];
  }
}

const p = new Person('Alice');
console.log(p.secret); // undefined
console.log(p.getSecret()); // 'mySecret'
  1. 避免属性名冲突 在扩展第三方对象时可用 Symbol 添加独立属性而不污染已有命名空间。
const tag = Symbol('tag');
const obj = {
  [tag]: 'custom'
};
  1. 模拟枚举类型
const COLOR = {
  RED: Symbol('red'),
  GREEN: Symbol('green')
};

function getColorName(color) {
  switch (color) {
    case COLOR.RED:
      return 'Red';
    case COLOR.GREEN:
      return 'Green';
    default:
      return 'Unknown';
  }
}
  1. 内置 Symbol(重要)

ES6 内置了一些 Symbol,用于实现语言底层行为:

  • Symbol.iterator: 实现可迭代协议

  • Symbol.toStringTag: 控制 Object.prototype.toString.call

  • Symbol.hasInstance: 控制 instanceof 行为

  • Symbol.toPrimitive: 控制对象转换为原始值的行为

const obj = {
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') return 42;
    return 'default';
  }
};

console.log(+obj); // 42
console.log(`${obj}`); // 'default'

四、总结

Symbol 是唯一且不可变的标识符,非常适合用于私有属性避免命名冲突和元编程。

常用于构建更安全、可扩展的模块或库,或增强对象行为的控制。

五、补充

ES6 提供了一组 内置 Symbol(Well-known Symbols),用于定制语言内部行为,也就是让普通对象可以参与 JavaScript 的底层机制。

这些内置 Symbol 都是挂在全局 Symbol 对象上的静态属性,如 Symbol.iteratorSymbol.toPrimitive 等。掌握它们可以让你写出更高级、更底层控制的 JavaScript 代码。

内置 Symbol作用典型使用场景
Symbol.iterator定义对象的默认迭代器行为for...of... 展开等
Symbol.toPrimitive定义对象转为原始值的方式+obj${obj}
Symbol.toStringTag控制 Object.prototype.toString.call(obj) 输出自定义类型标签
Symbol.hasInstance控制 instanceof 的行为自定义 instanceof
Symbol.isConcatSpreadable控制数组/类数组对象是否可被 concat 展开类数组转数组时
Symbol.species创建派生对象时指定构造函数自定义继承的返回类型
Symbol.match / Symbol.replace / Symbol.search / Symbol.split控制字符串方法的行为自定义正则-like 对象
Symbol.unscopables指定 with 语句中不暴露的属性避免冲突
Symbol.asyncIterator定义异步迭代器for await...of 迭代异步数据

常用内置 Symbol 详细说明 + 示例

  1. Symbol.iterator:让对象可被迭代
const obj = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let index = 0;
    const self = this;
    return {
      next() {
        return index < self.data.length
          ? { value: self.data[index++], done: false }
          : { done: true };
      }
    };
  }
};

for (const val of obj) {
  console.log(val); // 1, 2, 3
}
  1. Symbol.toPrimitive:对象转原始值时定制行为
const obj = {
  name: 'Test',
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') return 42;
    if (hint === 'string') return 'symbolic';
    return 'default';
  }
};

console.log(+obj); // 42
console.log(`${obj}`); // 'symbolic'
  1. Symbol.toStringTag:自定义对象类型标签
const obj = {
  [Symbol.toStringTag]: 'MyCustomType'
};

console.log(Object.prototype.toString.call(obj)); // [object MyCustomType]
  1. Symbol.hasInstance:定制 instanceof
class MyClass {
  static [Symbol.hasInstance](instance) {
    return 'isSpecial' in instance;
  }
}

const obj = { isSpecial: true };
console.log(obj instanceof MyClass); // true
  1. Symbol.isConcatSpreadable:控制 concat 展开行为
const arr = [1, 2];
arr[Symbol.isConcatSpreadable] = false;

console.log([0].concat(arr)); // [0, [1, 2]]
  1. Symbol.species:控制派生对象的构造函数
class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // 派生对象使用 Array 构造
  }
}

const a = new MyArray(1, 2, 3);
const b = a.map(x => x * 2);

console.log(b instanceof MyArray); // false
console.log(b instanceof Array);   // true
  1. Symbol.match / replace / search / split:模拟正则行为
const myMatcher = {
  [Symbol.match](str) {
    return str.includes('abc') ? ['abc'] : null;
  }
};

console.log('abcde'.match(myMatcher)); // ['abc']
  1. Symbol.asyncIterator:异步可迭代对象(ES2018)
const asyncIterable = {
  data: [1, 2, 3],
  async *[Symbol.asyncIterator]() {
    for (const val of this.data) {
      await new Promise(r => setTimeout(r, 100)); // simulate delay
      yield val;
    }
  }
};

(async () => {
  for await (const val of asyncIterable) {
    console.log(val); // 1, 2, 3
  }
})();
  1. Symbol.unscopables:控制 with 中的变量屏蔽
const obj = {
  foo: 1,
  bar: 2,
  [Symbol.unscopables]: {
    bar: true
  }
};

with (obj) {
  console.log(foo); // 1
  // console.log(bar); // ReferenceError: bar is not defined
}

总结

内置 SymbolJS 语言的底层扩展点,能让你控制对象如何参与语言特性(如迭代、类型转换等)。

日常开发中最常用的是:Symbol.iteratorSymbol.toPrimitiveSymbol.toStringTagSymbol.asyncIterator

它们广泛用于自定义框架行为、库内部实现、与语言机制对接(比如 polyfill、兼容性库等)。