Symbol
[TOC]
索引
构造方法:
- Symbol():
(description?)
,ES2015,原始数据类型,用于创建唯一标识符作为属性键,避免对象属性名冲突。
静态属性:
- Symbol.iterator:
()=>Iterator
,是 JS 中的内置 Symbol,它定义了对象的默认迭代器,是使对象可迭代的关键机制。
属性:
- sym.description:
string|undefined
,ES2020,只读,用于获取创建 Symbol 时传入的可选描述字符串。
静态方法:
- Symbol.for():
(description)
,用于在全局 Symbol 注册表中搜索或创建 Symbol。 - Symbol.keyFor():
(sym)
,用于检索全局 Symbol 注册表中 Symbol 关联键(描述字符串) 的方法。
Symbol
构造方法
Symbol()@
Symbol():(description?)
,ES2015,原始数据类型,用于创建唯一标识符作为属性键,避免对象属性名冲突。
description?:
any
,默认:undefined
,自动转字符串类型,Symbol 的描述文本。返回:
sym:
symbol
,总是返回一个全新前唯一的 Symbol 值(原始类型)。
核心特性:
唯一性:每次调用
Symbol()
都会创建一个全新的、唯一的 Symbol 值。jsconst sym1 = Symbol(); const sym2 = Symbol(); console.log(sym1 === sym2); // false // 相同描述仍产生不同 Symbol const sym3 = Symbol("desc"); const sym4 = Symbol("desc"); console.log(sym3 === sym4); // false
不可变性:Symbol 值一旦创建就不能被修改。
非构造函数:不能使用
new
操作符调用(会抛出错误)。jstry { new Symbol(); // 尝试使用 new } catch (e) { console.error(e); // TypeError: Symbol is not a constructor }
原始类型:Symbol 是 JavaScript 的第七种原始数据类型(与
number
、string
等并列)。不可枚举性:
Symbol作为键名:
- 不能通过 Object.keys() 或
for...in
遍历。 - 只能通过 Object.getOwnPropertySymbols() 或 Reflect.ownKeys() 遍历。
jsconst obj = { [Symbol('key')]: 'value', regularKey: 'regularValue' }; console.log(Object.keys(obj)); // ["regularKey"] console.log(Reflect.ownKeys(obj)); // ["regularKey", Symbol(key)]
- 不能通过 Object.keys() 或
JSON 序列化后被忽略:
由于 Symbol 不能隐式转换为字符串,导致它被 JSON 序列化后会被忽略。
jsconst obj = { [Symbol('id')]: 123, name: "Alice" }; console.log(JSON.stringify(obj)); // {"name":"Alice"}
类型检测:
可以通过判断
typeof value === 'symbol'
来检测是否是 Symbol 类型。jsconst sym = Symbol(); console.log(typeof sym); // "symbol" // 检测 Symbol 类型 if (typeof value === 'symbol') { // 处理 Symbol }
示例:
基本用法:
js// 创建匿名 Symbol const anonymousSymbol = Symbol(); // 创建带描述的 Symbol const idSymbol = Symbol("user_id"); // 作为对象属性键 const user = { name: "Alice", [idSymbol]: 12345 // 使用 Symbol 作为键 }; console.log(user[idSymbol]); // 12345
进阶扩展:
描述信息的使用:
虽然描述不影响 Symbol 的唯一性,但它对调试很有用:
jsconst sym = Symbol("debug_info"); // 获取描述(ES2019+) console.log(sym.description); // "debug_info" // 在 toString() 中体现 console.log(sym.toString()); // "Symbol(debug_info)"
内置 Symbol:
JavaScript 提供了多个内置 Symbol(称为 "well-known symbols"),用于自定义对象行为:
Symbol.iterator
:定义对象的默认迭代器Symbol.asyncIterator
:定义异步迭代器Symbol.toStringTag
:自定义 Object.prototype.toString() 返回值Symbol.species
:创建派生对象的构造函数Symbol.hasInstance
:自定义 instanceof 操作符行为
jsclass 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,它定义了对象的默认迭代器,是使对象可迭代的关键机制。
可迭代对象:
迭代协议:JS 中的标准迭代机制,包括:
- 可迭代协议:对象必须实现
Symbol.iterator
方法。 - 迭代器协议:迭代器必须实现
next()
方法。
- 可迭代协议:对象必须实现
语法结构:
[Symbol.iterator]
:作为对象的属性键,指向返回迭代器的函数。- 返回值:迭代器对象,该对象包含以下要素:
next()
方法:- 参数:无
- 返回值:迭代结果对象,包含:
value
:当前迭代值(任意类型)done
:布尔值,表示迭代是否完成
return()
方法(可选):在迭代提前终止时调用(如break
或抛出错误):throw()
方法(可选):在生成器迭代器中处理异常:
jsconst 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 可省略
可选的 throw() 方法:
jsconst 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("测试错误"); // 输出: "捕获错误: 测试错误"
迭代器重用:
每次调用
Symbol.iterator
返回新迭代器:jsconst 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 (新迭代器)
可迭代对象操作:
实现了可迭代协议的对象可用于以下操作:
for...of
循环- 解构赋值
- 扩展运算符
Array.from()
new Map()
等。
属性
description
sym.description:string|undefined
,ES2020,只读,用于获取创建 Symbol 时传入的可选描述字符串。
核心特性:
自动转字符串类型:
当使用非字符串值作为描述时,JavaScript 会自动转换为字符串:
jsconst 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"
对比
toString()
:虽然可以通过
toString()
获取包含描述的字符串,但description
更直接:jsconst colorSymbol = Symbol("color"); // 使用 toString() console.log(colorSymbol.toString()); // "Symbol(color)" // 使用 description console.log(colorSymbol.description); // "color" (仅描述部分)
示例:
基本用法:
js// 创建带描述的 Symbol const mySymbol = Symbol("description text"); // 访问描述 console.log(mySymbol.description); // "description text"
进阶扩展:
polyfill:
实现思路:截取
toString()
方法的返回值jsif (!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。
- description:
string
,自动转字符串类型,用于标识 Symbol 的描述文本。 - 返回:
- sym:
symbol|TypeError
,返回值:- 如果键存在于注册表:返回已注册的 Symbol。
- 如果键不存在于注册表:创建并注册新 Symbol。
- 无效调用(无参数):报
TypeError
错误。
核心特性:
对比
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
全局注册表:跨整个 JS 运行环境的 Symbol 存储
跨领域(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
示例:
基本使用:
jsconst Sym1 = Symbol.for("key"); const Sym2 = Symbol.for("key"); console.log(Sym1 === Sym2); // true
keyFor()
Symbol.keyFor():(sym)
,用于检索全局 Symbol 注册表中 Symbol 关联键(描述字符串) 的方法。
sym:
symbol
,要查找键(描述字符串)的全局 Symbol。返回:
description:
string|undefined|TypeError
,返回值:string
:参数是全局 Symbol,返回创建时使用的键。undefined
:参数是普通 Symbol 或内置Symbol,返回undefined
。TypeError
:参数不是 Symbol 或无参数,返回TypeError
。
核心特性:
参数必须传递 Symbol 类型:
jsSymbol.keyFor(123); // TypeError: 123 is not a symbol Symbol.keyFor("test"); // TypeError: "test" is not a symbol Symbol.keyFor(Symbol()); // 允许但返回 undefined
仅适用于全局 Symbol,普通 Symbol 返回
undefined
:- 全局 Symbol:通过
Symbol.for()
创建的Symbol。 - 普通 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
- 全局 Symbol:通过
对比
Symbol.for()
:Symbol.keyFor()
是Symbol.for()
的配套方法,用于获取全局 Symbol 的键:jsconst 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
内存管理考虑:
- 全局注册表中的 Symbol 不会被垃圾回收
- 避免创建过多全局 Symbol 导致内存泄漏
示例:
基本使用
jsconst globalSym = Symbol.for("app.id"); const localSym = Symbol("local"); console.log(Symbol.keyFor(globalSym)); // "app.id" console.log(Symbol.keyFor(localSym)); // undefined