JavaScript进阶④|Symbol与元编程,对象的隐藏身份
author: 专注前端开发分享JavaScript干货title: JavaScript进阶④Symbol与元编程对象的隐藏身份update: 2026-04-28tags: JavaScript,Symbol,元编程,唯一标识,内置Symbol,前端进阶作者专注前端开发分享JavaScript干货更新时间2026年4月适合人群掌握ES6基础想了解Symbol和元编程的开发者前言Symbol是什么Symbol是 ES6 引入的一种新的原始数据类型表示唯一的值。元编程是指操作程序本身对象、类、函数的编程方式。一、Symbol 基础1.1 创建 Symbol// 创建 Symbol可选描述符constsym1Symbol();constsym2Symbol(描述符);constsym3Symbol(描述符);console.log(sym1);// Symbol()console.log(sym2);// Symbol(描述符)console.log(sym2sym3);// false每个Symbol都是唯一的// 描述符只是用于调试不影响唯一性console.log(sym2.toString());// Symbol(描述符)console.log(sym2.description);// 描述符ES20191.2 Symbol 作为对象属性键constidSymbol(id);constuser{name:张三,[id]:12345// Symbol 作为属性键};console.log(user.name);// 张三console.log(user[id]);// 12345// 注意Symbol 键不会被常规方法枚举到console.log(Object.keys(user));// [name]console.log(Object.getOwnPropertyNames(user));// [name]console.log(Object.getOwnPropertySymbols(user));// [Symbol(id)]二、全局 Symbol 注册表// Symbol.for(key)从全局注册表获取或创建 Symbolconstsym1Symbol.for(app.id);// 创建并注册constsym2Symbol.for(app.id);// 从注册表获取console.log(sym1sym2);// true ✅相同的 key 返回相同的 Symbol// Symbol.keyFor(sym)获取全局 Symbol 的 keyconsole.log(Symbol.keyFor(sym1));// app.idconsole.log(Symbol.keyFor(Symbol(test)));// undefined非全局 Symbol三、内置 Symbol元编程关键JavaScript 内置了许多 Symbol 值用于改变对象的行为。3.1 Symbol.iterator可迭代// 让对象可迭代constmyObj{data:[1,2,3],[Symbol.iterator](){letindex0;return{next:(){if(indexthis.data.length){return{value:this.data[index],done:false};}return{value:undefined,done:true};}};}};for(constitemofmyObj){console.log(item);// 1, 2, 3}3.2 Symbol.toPrimitive类型转换// 控制对象转原始值的行为constmoney{value:100,[Symbol.toPrimitive](hint){console.log(hint:${hint});// number、string 或 defaultif(hintnumber){returnthis.value;}if(hintstring){return¥${this.value};}returnthis.value;}};console.log(money);// hint: number → 100console.log(${money});// hint: string → ¥100console.log(money10);// hint: default → 1103.3 Symbol.toStringTagtoString// 改变 Object.prototype.toString.call(obj) 的结果constmyObj{[Symbol.toStringTag]:MyObject};console.log(Object.prototype.toString.call(myObj));// [object MyObject]// 内置对象的例子console.log(Object.prototype.toString.call([]));// [object Array]console.log(Object.prototype.toString.call(newMap()));// [object Map]3.4 其他常用内置 Symbol// Symbol.hasInstanceinstanceof 操作符classMyClass{static[Symbol.hasInstance](instance){returninstanceinstanceofArray;// 自定义 instanceof 行为}}console.log([]instanceofMyClass);// true// Symbol.isConcatSpreadable控制 concat 是否展开constarr[1,2,3];arr[Symbol.isConcatSpreadable]false;console.log([].concat(arr));// [[1, 2, 3]]不展开// Symbol.species创建派生对象时使用的构造函数classMyArrayextendsArray{staticget[Symbol.species](){returnArray;// 用 Array 而不是 MyArray 创建新数组}}constmyArrnewMyArray(1,2,3);constmappedmyArr.map(xx*2);console.log(mappedinstanceofMyArray);// false是 Array四、实战用 Symbol 实现私有属性// 模块内部const_widthSymbol(width);const_heightSymbol(height);classRectangle{constructor(width,height){this[_width]width;this[_height]height;}getarea(){returnthis[_width]*this[_height];}resize(width,height){this[_width]width;this[_height]height;}}constrectnewRectangle(10,20);console.log(rect.area);// 200// 外部无法直接访问私有属性除非知道 Symbol// console.log(rect._width); // undefined ❌// 但可以通过 Object.getOwnPropertySymbols 获取constsymsObject.getOwnPropertySymbols(rect);console.log(rect[syms[0]]);// 10可以拿到但很不方便五、实战用 Symbol 做事件发射器避免冲突// 用 Symbol 作为事件名避免字符串冲突constEVENTS{CONNECT:Symbol(connect),DISCONNECT:Symbol(disconnect),MESSAGE:Symbol(message)};classEventEmitter{constructor(){this.listenersnewMap();}on(event,callback){if(!this.listeners.has(event)){this.listeners.set(event,[]);}this.listeners.get(event).push(callback);}emit(event,...args){constcallbacksthis.listeners.get(event);if(callbacks){callbacks.forEach(cbcb(...args));}}}constemitternewEventEmitter();emitter.on(EVENTS.CONNECT,()console.log(连接成功));emitter.emit(EVENTS.CONNECT);// 连接成功六、知识卡Symbol 方法/属性说明Symbol()创建唯一 SymbolSymbol.for(key)从全局注册表获取/创建Symbol.keyFor(sym)获取全局 Symbol 的 keySymbol.iterator使对象可迭代Symbol.toPrimitive控制类型转换Symbol.toStringTag改变 toString 结果Object.getOwnPropertySymbols()获取对象的 Symbol 键七、课后作业用 Symbol 实现一个对象的私有方法外部无法直接调用用Symbol.hasInstance自定义一个类的instanceof行为用Symbol.toPrimitive实现一个可以参与数学运算的货币对象有问题欢迎评论区留言大家一起讨论标签JavaScript | Symbol | 元编程 | 唯一标识 | 内置Symbol | 前端进阶