es6中的symbol基础知识
ES6 中的 Symbol
是一种新的原始数据类型(Primitive Data Type),它代表唯一的、不可变的值。它的主要目的是为了解决属性名冲突的问题,并为对象定义非字符串的属性键(Key)。
以下是 Symbol
的核心特性和用法:
1. 创建 Symbol
- 使用
Symbol()
函数创建,每次调用都会返回一个独一无二的值:const sym1 = Symbol(); const sym2 = Symbol(); console.log(sym1 === sym2); // false
- 可以传入一个可选的字符串参数作为描述(Description),主要用于调试目的,不影响唯一性:
const sym3 = Symbol('description'); const sym4 = Symbol('description'); console.log(sym3 === sym4); // false (即使描述相同,值也不同)
2. 核心特性
- 唯一性: 每个
Symbol
值都是唯一的,无论描述是否相同。这是其最核心的特性。 - 不可变性:
Symbol
值一旦创建就不能被修改。 - 原始类型:
typeof
操作符返回'symbol'
。console.log(typeof Symbol()); // 'symbol'
3. 主要用途:对象属性键
Symbol
的主要价值在于作为对象的属性名(Key)。- 使用 Symbol 作为属性名可以避免命名冲突,尤其是在扩展第三方库的对象或定义对象内部的元属性时非常有用。
- 定义 Symbol 属性:
const mySymbol = Symbol('myKey'); const obj = {}; // 方法1: 直接使用方括号 obj[mySymbol] = 'value'; // 方法2: 在对象字面量中定义 (需要方括号) const obj2 = {[mySymbol]: 'value' }; // 方法3: 使用 Object.defineProperty Object.defineProperty(obj, mySymbol, { value: 'value' });
- 访问 Symbol 属性:
console.log(obj[mySymbol]); // 'value'
- 重要:Symbol 属性在常规遍历中不可见:
for...in
循环不会枚举 Symbol 属性。Object.keys(obj)
不会返回 Symbol 属性。Object.getOwnPropertyNames(obj)
不会返回 Symbol 属性。- 需要使用
Object.getOwnPropertySymbols(obj)
来获取对象自身的所有 Symbol 属性:const symbols = Object.getOwnPropertySymbols(obj); console.log(symbols); // [ Symbol(myKey) ] console.log(obj[symbols[0]]); // 'value'
Reflect.ownKeys(obj)
会返回所有类型的键(包括字符串和 Symbol):console.log(Reflect.ownKeys(obj)); // [ ...other keys..., Symbol(myKey) ]
4. 全局 Symbol 注册表
Symbol.for(key)
:在全局 Symbol 注册表中查找或创建一个与给定字符串key
关联的 Symbol。- 如果存在与
key
关联的 Symbol,则返回它。 - 如果不存在,则创建一个新的 Symbol 并与
key
关联后返回。 - 相同
key
调用Symbol.for()
总是返回同一个 Symbol。
const globSym1 = Symbol.for('globalKey'); const globSym2 = Symbol.for('globalKey'); console.log(globSym1 === globSym2); // true
- 如果存在与
Symbol.keyFor(sym)
:查询全局注册表,返回给定 Symbol 关联的字符串键(如果该 Symbol 是通过Symbol.for()
创建并注册的)。如果不是全局注册的 Symbol,则返回undefined
。console.log(Symbol.keyFor(globSym1)); // 'globalKey' console.log(Symbol.keyFor(sym1)); // undefined (sym1 不是全局注册的)
5. 内置 Symbol 值 (Well-known Symbols)
- ES6 定义了一系列内置的 Symbol 值,它们代表了语言内部的、对象可定制的方法或行为。这些 Symbol 存储在
Symbol
的静态属性上。 - 常见的内置 Symbol:
Symbol.iterator
: 定义对象的默认迭代器。被for...of
循环使用。Symbol.hasInstance
: 自定义instanceof
操作符的行为。Symbol.toStringTag
: 定义Object.prototype.toString.call()
返回的字符串[object XXXX]
中的XXXX
。Symbol.isConcatSpreadable
: 控制数组或类数组对象在Array.prototype.concat()
中是否被展开。Symbol.species
: 指定创建派生对象(如map
,filter
返回的新数组)时使用的构造函数。Symbol.toPrimitive
: 定义对象如何被转换为原始值(在涉及+
,==
,String()
,Number()
等操作时)。Symbol.match
/Symbol.replace
/Symbol.search
/Symbol.split
: 自定义对象在作为String.prototype.match()
/replace()
/search()
/split()
方法的第一个参数时的行为。
- 示例 (自定义
toStringTag
):class MyCollection {get [Symbol.toStringTag]() {return 'MyAwesomeCollection';} } const coll = new MyCollection(); console.log(Object.prototype.toString.call(coll)); // '[object MyAwesomeCollection]'
6. 注意事项
- 类型转换: Symbol 不能隐式转换为字符串或数字(尝试会抛出
TypeError
)。如果需要字符串表示,必须显式调用.toString()
或使用.description
属性(ES2019+)。const sym = Symbol('desc'); console.log(sym.toString()); // 'Symbol(desc)' console.log(sym.description); // 'desc' (ES2019+) // console.log('Symbol: ' + sym); // TypeError!
- JSON 序列化:
JSON.stringify()
会完全忽略对象的 Symbol 属性键和值。 - 不是真正的私有: 虽然 Symbol 属性在常规遍历中不可见,但通过
Object.getOwnPropertySymbols()
或Reflect.ownKeys()
仍然可以获取到。它们提供的是非冲突的、半隐藏的属性,而非严格的私有属性(真正的私有属性需要 ES2022+ 的#
语法)。
总结
ES6 的 Symbol
是一种用于创建唯一标识符的原始数据类型。它的核心价值在于:
- 创建唯一属性键: 从根本上避免对象属性名冲突,特别适合库开发、元编程和定义对象内部特殊行为。
- 定义内置行为: 通过内置 Symbol(如
Symbol.iterator
,Symbol.toStringTag
)暴露语言的内部机制,允许开发者自定义对象在特定操作(迭代、类型转换、字符串匹配等)中的行为。 - 全局共享符号: 通过
Symbol.for()
和Symbol.keyFor()
实现在不同作用域或模块间共享相同的 Symbol。
理解 Symbol
及其应用(尤其是内置 Symbol)是掌握现代 JavaScript 高级特性和元编程能力的关键一步。