1. Symbol 基本概念
1.1 创建 Symbol
const s1 = Symbol ( ) ;
const s2 = Symbol ( 'description' ) ;
console. log ( Symbol ( ) === Symbol ( ) ) ;
console. log ( Symbol ( 'key' ) === Symbol ( 'key' ) ) ;
1.2 Symbol.for() 和 Symbol.keyFor()
const s1 = Symbol. for ( 'shared' ) ;
const s2 = Symbol. for ( 'shared' ) ;
console. log ( s1 === s2) ;
console. log ( Symbol. keyFor ( s1) ) ;
console. log ( Symbol. keyFor ( Symbol ( 'not shared' ) ) ) ;
2. Symbol 作为对象属性
2.1 基本用法
const mySymbol = Symbol ( 'mySymbol' ) ;
const obj = {
[ mySymbol] : 'Hello Symbol'
} ;
console. log ( obj[ mySymbol] ) ;
obj[ Symbol ( 'anotherSymbol' ) ] = 'Another value' ;
2.2 Symbol 属性的特点
const obj = {
[ Symbol ( 'name' ) ] : 'John' ,
age : 25
} ;
console. log ( Object. keys ( obj) ) ;
console. log ( Object. getOwnPropertyNames ( obj) ) ;
console. log ( Object. getOwnPropertySymbols ( obj) ) ;
console. log ( Reflect. ownKeys ( obj) ) ;
3. 内置 Symbol 值
3.1 Symbol.iterator 迭代器
const collection = {
items : [ 'item1' , 'item2' , 'item3' ] ,
[ Symbol. iterator] ( ) {
let index = 0 ;
return {
next : ( ) => {
return {
done : index >= this . items. length,
value : this . items[ index++ ]
} ;
}
} ;
}
} ;
for ( const item of collection) {
console. log ( item) ;
}
3.2 Symbol.toStringTag
class CustomClass {
get [ Symbol. toStringTag] ( ) {
return 'CustomClass' ;
}
}
const obj = new CustomClass ( ) ;
console. log ( Object . prototype. toString . call ( obj) ) ;
3.3 Symbol.species
class MyArray extends Array {
static get [ Symbol. species] ( ) {
return Array;
}
}
const arr = new MyArray ( 1 , 2 , 3 ) ;
const mapped = arr. map ( x => x * 2 ) ;
4. 实际应用场景
4.1 私有属性模拟
const privateProperty = Symbol ( 'privateProperty' ) ;
const privateMethod = Symbol ( 'privateMethod' ) ;
class MyClass {
constructor ( ) {
this [ privateProperty] = 'private value' ;
}
[ privateMethod] ( ) {
return 'private method' ;
}
publicMethod ( ) {
return this [ privateMethod] ( ) ;
}
}
const instance = new MyClass ( ) ;
console. log ( instance[ privateProperty] ) ;
4.2 防止属性名冲突
const librarySymbol = Symbol ( 'myLibrary' ) ;
class ThirdPartyClass {
constructor ( ) {
this [ librarySymbol] = {
config : { } ,
cache : new Map ( )
} ;
}
}
4.3 实现特定的接口或协议
const protocol = {
connect : Symbol ( 'connect' ) ,
disconnect : Symbol ( 'disconnect' )
} ;
class Device {
[ protocol. connect] ( ) {
console. log ( 'Device connected' ) ;
}
[ protocol. disconnect] ( ) {
console. log ( 'Device disconnected' ) ;
}
}
5. Symbol 与元编程
5.1 自定义对象的基本行为
const myObject = {
[ Symbol. toPrimitive] ( hint ) {
switch ( hint) {
case 'number' :
return 42 ;
case 'string' :
return 'custom string' ;
default :
return 'default' ;
}
}
} ;
console. log ( + myObject) ;
console. log ( \`\${ myObject} \`) ;
5.2 自定义对象的检查行为
const customInspect = Symbol. for ( 'nodejs.util.inspect.custom' ) ;
class CustomObject {
[ customInspect] ( ) {
return 'CustomObject(...)' ;
}
}
console. log ( new CustomObject ( ) ) ;
6. 最佳实践
6.1 使用建议
const userToken = Symbol ( 'user authentication token' ) ;
const STATUS = {
PENDING : Symbol ( 'pending' ) ,
FULFILLED : Symbol ( 'fulfilled' ) ,
REJECTED : Symbol ( 'rejected' )
} ;
const _data = Symbol ( 'private data' ) ;
class MyClass {
constructor ( ) {
this [ _data] = { } ;
}
}
6.2 注意事项
const sym = new Symbol ( ) ;
const sym = Symbol ( 'my symbol' ) ;
alert ( sym) ;
alert ( sym. toString ( ) ) ;
alert ( sym. description) ;
const obj = {
[ Symbol ( 'id' ) ] : 1 ,
name : 'John'
} ;
console. log ( JSON . stringify ( obj) ) ;
7. Symbol 的遍历方法
7.1 不同遍历方法的对比
const obj = {
name : 'John' ,
age : 25 ,
[ Symbol ( 'id' ) ] : 1 ,
[ Symbol ( 'key' ) ] : 'value'
} ;
console. log ( Object. keys ( obj) ) ;
console. log ( Object. getOwnPropertyNames ( obj) ) ;
console. log ( Object. getOwnPropertySymbols ( obj) ) ;
console. log ( Reflect. ownKeys ( obj) ) ;
7.2 实际应用示例
7.2.1 获取对象的所有 Symbol 属性
function getAllSymbolProperties ( obj ) {
return Object. getOwnPropertySymbols ( obj)
. reduce ( ( result, symbol ) => {
result[ symbol. description] = obj[ symbol] ;
return result;
} , { } ) ;
}
const user = {
name : 'John' ,
[ Symbol ( 'id' ) ] : 123 ,
[ Symbol ( 'role' ) ] : 'admin'
} ;
console. log ( getAllSymbolProperties ( user) ) ;
7.2.2 合并包含 Symbol 的对象
function mergeWithSymbols ( target, ... sources ) {
sources. forEach ( source => {
Object. assign ( target, source) ;
Object. getOwnPropertySymbols ( source) . forEach ( symbol => {
target[ symbol] = source[ symbol] ;
} ) ;
} ) ;
return target;
}
const baseConfig = {
name : 'config' ,
[ Symbol ( 'secret' ) ] : 'xyz'
} ;
const extraConfig = {
version : '1.0' ,
[ Symbol ( 'key' ) ] : '123'
} ;
const finalConfig = mergeWithSymbols ( { } , baseConfig, extraConfig) ;
console. log ( Reflect. ownKeys ( finalConfig) ) ;
7.2.3 深度克隆包含 Symbol 的对象
function deepCloneWithSymbols ( obj ) {
if ( obj === null || typeof obj !== 'object' ) {
return obj;
}
const clone = Array. isArray ( obj) ? [ ] : { } ;
Reflect. ownKeys ( obj) . forEach ( key => {
clone[ key] = deepCloneWithSymbols ( obj[ key] ) ;
} ) ;
return clone;
}
const original = {
name : 'test' ,
[ Symbol ( 'id' ) ] : 1 ,
nested : {
[ Symbol ( 'key' ) ] : 'value'
}
} ;
const cloned = deepCloneWithSymbols ( original) ;
console. log (
Object. getOwnPropertySymbols ( cloned) . length ===
Object. getOwnPropertySymbols ( original) . length
) ;
7.3 遍历方法的使用建议
function processAllProperties ( obj ) {
Reflect. ownKeys ( obj) . forEach ( key => {
console. log ( key, obj[ key] ) ;
} ) ;
}
function processSymbolsOnly ( obj ) {
Object. getOwnPropertySymbols ( obj) . forEach ( symbol => {
console. log ( symbol. description, obj[ symbol] ) ;
} ) ;
}
function processPropertiesSeparately ( obj ) {
Object. keys ( obj) . forEach ( key => {
console. log ( 'Regular:' , key, obj[ key] ) ;
} ) ;
Object. getOwnPropertySymbols ( obj) . forEach ( symbol => {
console. log ( 'Symbol:' , symbol. description, obj[ symbol] ) ;
} ) ;
}
7.4 性能考虑
function optimizedPropertyProcessing ( obj ) {
const symbols = Object. getOwnPropertySymbols ( obj) ;
if ( symbols. length > 0 ) {
const symbolProps = { } ;
symbols. forEach ( symbol => {
symbolProps[ symbol. description] = obj[ symbol] ;
} ) ;
}
const regularKeys = Object. keys ( obj) ;
const batchSize = 1000 ;
for ( let i = 0 ; i < regularKeys. length; i += batchSize) {
const batch = regularKeys. slice ( i, i + batchSize) ;
batch. forEach ( key => {
} ) ;
}
}