Skip to content

Symbol

[TOC]

索引

构造方法

  • Symbol()(description?)ES2015原始数据类型,用于创建唯一标识符作为属性键,避免对象属性名冲突。

静态属性

  • Symbol.iterator()=>Iterator,是 JS 中的内置 Symbol,它定义了对象的默认迭代器,是使对象可迭代的关键机制。

属性

  • sym.descriptionstring|undefinedES2020只读,用于获取创建 Symbol 时传入的可选描述字符串

静态方法

  • Symbol.for()(description),用于在全局 Symbol 注册表中搜索或创建 Symbol
  • Symbol.keyFor()(sym),用于检索全局 Symbol 注册表中 Symbol 关联键(描述字符串) 的方法。

Symbol

构造方法

Symbol()@

Symbol()(description?)ES2015原始数据类型,用于创建唯一标识符作为属性键,避免对象属性名冲突。

  • description?any默认:undefined自动转字符串类型,Symbol 的描述文本。

  • 返回:

  • symsymbol,总是返回一个全新前唯一的 Symbol 值(原始类型)。

核心特性

  1. 唯一性:每次调用 Symbol() 都会创建一个全新的、唯一的 Symbol 值。

    js
    const sym1 = Symbol();
    const sym2 = Symbol();
    console.log(sym1 === sym2); // false
    
    // 相同描述仍产生不同 Symbol
    const sym3 = Symbol("desc");
    const sym4 = Symbol("desc");
    console.log(sym3 === sym4); // false
  2. 不可变性:Symbol 值一旦创建就不能被修改。

  3. 非构造函数:不能使用 new 操作符调用(会抛出错误)。

    js
    try {
      new Symbol(); // 尝试使用 new
    } catch (e) {
      console.error(e); // TypeError: Symbol is not a constructor
    }
  4. 原始类型:Symbol 是 JavaScript 的第七种原始数据类型(与 numberstring 等并列)。

  5. 不可枚举性

    Symbol作为键名:

    js
    const obj = {
      [Symbol('key')]: 'value',
      regularKey: 'regularValue'
    };
    
    console.log(Object.keys(obj)); // ["regularKey"]
    console.log(Reflect.ownKeys(obj)); // ["regularKey", Symbol(key)]
  6. JSON 序列化后被忽略

    由于 Symbol 不能隐式转换为字符串,导致它被 JSON 序列化后会被忽略。

    js
    const obj = {
      [Symbol('id')]: 123,
      name: "Alice"
    };
    
    console.log(JSON.stringify(obj)); // {"name":"Alice"}
  7. 类型检测

    可以通过判断 typeof value === 'symbol' 来检测是否是 Symbol 类型。

    js
    const sym = Symbol();
    console.log(typeof sym); // "symbol"
    
    // 检测 Symbol 类型
    if (typeof value === 'symbol') {
      // 处理 Symbol
    }

示例

  1. 基本用法

    js
    // 创建匿名 Symbol
    const anonymousSymbol = Symbol();
    
    // 创建带描述的 Symbol
    const idSymbol = Symbol("user_id");
    
    // 作为对象属性键
    const user = {
      name: "Alice",
      [idSymbol]: 12345 // 使用 Symbol 作为键
    };
    
    console.log(user[idSymbol]); // 12345

进阶扩展

  1. 描述信息的使用

    虽然描述不影响 Symbol 的唯一性,但它对调试很有用:

    js
    const sym = Symbol("debug_info");
    
    // 获取描述(ES2019+)
    console.log(sym.description); // "debug_info"
    
    // 在 toString() 中体现
    console.log(sym.toString()); // "Symbol(debug_info)"
  2. 内置 Symbol

    JavaScript 提供了多个内置 Symbol(称为 "well-known symbols"),用于自定义对象行为:

    • Symbol.iterator:定义对象的默认迭代器
    • Symbol.asyncIterator:定义异步迭代器
    • Symbol.toStringTag:自定义 Object.prototype.toString() 返回值
    • Symbol.species:创建派生对象的构造函数
    • Symbol.hasInstance:自定义 instanceof 操作符行为
    js
    class CustomCollection {
      constructor(items) {
        this.items = items;
      }
      
      // 自定义迭代规则
      [Symbol.iterator]() {
        let index = 0;
        return {
          next: () => {
            return index < this.items.length
              ? { value: this.items[index++], done: false }
              : { done: true };
          }
        };
      }
    }
    
    const collection = new CustomCollection([1, 2, 3]);
    for (const item of collection) {
      console.log(item); // 1, 2, 3
    }

静态属性

iterator

Symbol.iterator()=>Iterator,是 JS 中的内置 Symbol,它定义了对象的默认迭代器,是使对象可迭代的关键机制。

可迭代对象

  1. 迭代协议:JS 中的标准迭代机制,包括:

    • 可迭代协议:对象必须实现 Symbol.iterator 方法。
    • 迭代器协议:迭代器必须实现 next() 方法。
  2. 语法结构

    • [Symbol.iterator]:作为对象的属性键,指向返回迭代器的函数。
    • 返回值:迭代器对象,该对象包含以下要素:
      • next() 方法
        • 参数:无
        • 返回值:迭代结果对象,包含:
          • value:当前迭代值(任意类型)
          • done:布尔值,表示迭代是否完成
      • return() 方法(可选):在迭代提前终止时调用(如 break 或抛出错误):
      • throw() 方法(可选):在生成器迭代器中处理异常:
    js
    const range = {
      start: 1,
      end: 3,
      
      [Symbol.iterator]() { // 无参数
        let current = this.start;
          
        // 迭代器创建逻辑
        return {
          // 1. next()方法
          next: () => {
            // 返回迭代结果
            if (current <= this.end) {
              return { value: current++, done: false };
            }
            return { done: true };
          },
            
          // 2. return()方法
          return() {
            console.log('迭代提前终止');
            return { done: true };
          },
            
          // 3. throw()方法
          throw(e) { /* 错误处理 */ }
        };
      }
    };
    js
    // 迭代结果示例
    
    // 迭代中
    { value: 1, done: false }
    { value: 2, done: false }
    { value: 3, done: false }
    
    // 迭代结束
    { value: undefined, done: true } // value 可省略
  3. 可选的 throw() 方法

    js
    const errorGenerator = {
      *[Symbol.iterator]() {
        try {
          yield 1;
          yield 2;
        } catch (e) {
          console.log(`捕获错误: ${e}`);
        }
      }
    };
    
    const gen = errorGenerator[Symbol.iterator]();
    gen.next(); // { value: 1, done: false }
    gen.throw("测试错误"); // 输出: "捕获错误: 测试错误"
  4. 迭代器重用

    每次调用 Symbol.iterator 返回新迭代器:

    js
    const sequence = {
      [Symbol.iterator]() {
        let count = 1;
        return {
          next() {
            return { value: count++, done: false };
          }
        };
      }
    };
    
    const iter1 = sequence[Symbol.iterator]();
    const iter2 = sequence[Symbol.iterator]();
    
    console.log(iter1.next().value); // 1
    console.log(iter2.next().value); // 1 (新迭代器)
  5. 可迭代对象操作

    实现了可迭代协议的对象可用于以下操作:

    • for...of循环
    • 解构赋值
    • 扩展运算符
    • Array.from()
    • new Map() 等。

属性

description

sym.descriptionstring|undefinedES2020只读,用于获取创建 Symbol 时传入的可选描述字符串

核心特性

  1. 自动转字符串类型

    当使用非字符串值作为描述时,JavaScript 会自动转换为字符串:

    js
    const sym1 = Symbol(42);
    console.log(sym1.description); // "42" (数字转字符串)
    
    const sym2 = Symbol({ name: "John" });
    console.log(sym2.description); // "[object Object]"
    
    const sym3 = Symbol(null);
    console.log(sym3.description); // "null"
  2. 对比 toString()

    虽然可以通过 toString() 获取包含描述的字符串,description 更直接

    js
    const colorSymbol = Symbol("color");
    
    // 使用 toString()
    console.log(colorSymbol.toString()); // "Symbol(color)" 
    
    // 使用 description
    console.log(colorSymbol.description); // "color" (仅描述部分)

示例

  1. 基本用法

    js
    // 创建带描述的 Symbol
    const mySymbol = Symbol("description text");
    
    // 访问描述
    console.log(mySymbol.description); // "description text"

进阶扩展

  1. polyfill

    实现思路:截取 toString() 方法的返回值

    js
    if (!Symbol.prototype.description) {
      Object.defineProperty(Symbol.prototype, 'description', {
        get: function() {
          const str = this.toString();
          return str.slice(7, -1) || undefined;
        }
      });
    }

静态方法

for()@

Symbol.for()(description),用于在全局 Symbol 注册表中搜索或创建 Symbol

  • descriptionstring自动转字符串类型,用于标识 Symbol 的描述文本。
  • 返回:
  • symsymbol|TypeError,返回值:
    • 如果键存在于注册表:返回已注册的 Symbol。
    • 如果键不存在于注册表:创建并注册新 Symbol。
    • 无效调用(无参数):报 TypeError 错误。

核心特性

  1. 对比 Symbol()/Symbol.keyFor()

    方法作用域唯一性键检索跨领域
    Symbol()局部每次调用都唯一不支持不共享
    Symbol.for()全局相同键返回相同 Symbol支持共享
    Symbol.keyFor()全局仅用于已注册 Symbol专门用于检索键共享
    js
    // 常规 Symbol(每次创建都不同)
    const localSym1 = Symbol("key");
    const localSym2 = Symbol("key");
    console.log(localSym1 === localSym2); // false
    
    // 全局注册 Symbol(相同 key 返回相同 Symbol)
    const globalSym1 = Symbol.for("key");
    const globalSym2 = Symbol.for("key");
    console.log(globalSym1 === globalSym2); // true
  2. 全局注册表:跨整个 JS 运行环境的 Symbol 存储

  3. 跨领域(realm)共享

    在不同 iframe、service worker 等环境中共享相同的 Symbol

    js
    // 在 iframe 中
    const iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    
    iframe.contentWindow.eval(`
      window.iframeSymbol = Symbol.for('shared');
    `);
    
    // 在主页面中
    const mainSymbol = Symbol.for('shared');
    
    console.log(mainSymbol === iframe.contentWindow.iframeSymbol); // true

示例

  1. 基本使用

    js
    const Sym1 = Symbol.for("key");
    const Sym2 = Symbol.for("key");
    console.log(Sym1 === Sym2); // true

keyFor()

Symbol.keyFor()(sym),用于检索全局 Symbol 注册表中 Symbol 关联键(描述字符串) 的方法。

  • symsymbol,要查找键(描述字符串)的全局 Symbol。

  • 返回:

  • descriptionstring|undefined|TypeError,返回值:

    • string:参数是全局 Symbol,返回创建时使用的键。
    • undefined:参数是普通 Symbol 或内置Symbol,返回 undefined
    • TypeError:参数不是 Symbol 或无参数,返回 TypeError

核心特性

  1. 参数必须传递 Symbol 类型

    js
    Symbol.keyFor(123);        // TypeError: 123 is not a symbol
    Symbol.keyFor("test");     // TypeError: "test" is not a symbol
    Symbol.keyFor(Symbol());   // 允许但返回 undefined
  2. 仅适用于全局 Symbol,普通 Symbol 返回 undefined

    1. 全局 Symbol:通过 Symbol.for() 创建的Symbol。
    2. 普通 Symbol:通过 Symbol() 创建的Symbol。
    js
    // 全局 Symbol
    const globalSym = Symbol.for("app.key");
    Symbol.keyFor(globalSym); // "app.key"
    
    // 普通 Symbol
    const localSym = Symbol("local.key");
    Symbol.keyFor(localSym);  // undefined
  3. 对比 Symbol.for()

    Symbol.keyFor()Symbol.for() 的配套方法,用于获取全局 Symbol 的键:

    js
    const globalSym = Symbol.for("app.id");
    
    // 获取全局 Symbol 的键
    console.log(Symbol.keyFor(globalSym)); // "app.id"
    
    // 对非全局 Symbol 返回 undefined
    const localSym = Symbol("local");
    console.log(Symbol.keyFor(localSym)); // undefined
  4. 内存管理考虑

    • 全局注册表中的 Symbol 不会被垃圾回收
    • 避免创建过多全局 Symbol 导致内存泄漏

示例

  1. 基本使用

    js
    const globalSym = Symbol.for("app.id");
    const localSym = Symbol("local");
    
    console.log(Symbol.keyFor(globalSym)); // "app.id"
    console.log(Symbol.keyFor(localSym)); // undefined