焦作市网站建设_网站建设公司_Ruby_seo优化
2026/1/7 20:38:42 网站建设 项目流程


Symbol不是摆设:前端老铁们怎么用它解决实际问题

  • Symbol不是摆设:前端老铁们怎么用它解决实际问题
    • 为啥突然聊 Symbol?
    • Symbol 到底是个啥玩意儿
    • 创建 Symbol 的几种姿势
      • 1. 裸奔创建:Symbol(description)
      • 2. 全局登记:Symbol.for(key)
      • 3. 反向查询:Symbol.keyFor(sym)
    • Symbol 当对象属性名有多爽
    • 内置 Symbol 那些隐藏彩蛋
      • 1. 让对象“可迭代”:Symbol.iterator
      • 2. 自定义 toString:Symbol.toStringTag
      • 3. 自动拆箱:Symbol.toPrimitive
    • Symbol 在真实项目里能干点啥
      • 场景 1:Redux ActionType 防冲突
      • 场景 2:插件扩展点
      • 场景 3:私有方法“伪实现”
    • 踩过的坑比写过的代码还多
    • 调试 Symbol 时的骚操作
    • 让 Symbol 更好用的小技巧
      • 1. 工厂函数封装
      • 2. TypeScript 声明不闹心
      • 3. 团队协作“符号常量”管理
    • 你以为 Symbol 冷门?其实框架早用烂了

Symbol不是摆设:前端老铁们怎么用它解决实际问题

写在前面:
这篇文章是边撸串边写的,逻辑可能跳,但代码保真。——来自一个被 Symbol 坑过 37 次、至今仍在坑里蹦迪的底层前端


为啥突然聊 Symbol?

前阵子组里新来的小老弟问我:
“哥,这破项目里怎么那么多Symbol('__internal__')?是不是前人吃饱了撑的?”
我当场把他键盘抢过来,指着一行Symbol.for('antd.table.row')说:
“兄弟,这行代码救过咱线上三次,你居然说它没用?”

于是就有了这篇吐槽——Symbol 真不是花架子,只是它太低调,低调到连报错都不带响的。


Symbol 到底是个啥玩意儿

一句话:它是 JS 里唯一能做到字面量级别唯一的原始值。
typeof给你返个'symbol',连面试官都愣半秒——“还有这类型?”

// 1. 创建consts1=Symbol('desc');consts2=Symbol('desc');s1===s2;// false,描述只是调试用的,不参与判等// 2. typeofconsole.log(typeofs1);// 'symbol',不是 'string',不是 'object'// 3. 不能 new// const bad = new Symbol(); // ❌ 直接抛错,JS 怕你搞出装箱幺蛾子

创建 Symbol 的几种姿势

1. 裸奔创建:Symbol(description)

constmySecret=Symbol('mySecret');// 每次运行都独一无二

2. 全局登记:Symbol.for(key)

consta=Symbol.for('globalKey');constb=Symbol.for('globalKey');a===b;// true,全局注册表帮你复用

3. 反向查询:Symbol.keyFor(sym)

constsym=Symbol.for('moduleA/toast');console.log(Symbol.keyFor(sym));// 'moduleA/toast'

坑点预警
Symbol.for会把 key 扔到全局跨 iframe 共享的注册表里,所以别把用户隐私当 key,不然隔壁 iframe 都能读到。


Symbol 当对象属性名有多爽

先上需求:
“写一个缓存工具,内部属性不想被外部 for-in 扫到,也不想被 JSON.stringify 序列化。”

// cache.jsconstSIZE=Symbol('size');constCACHE=Symbol('cache');classLruCache{constructor(max=100){this[MAX]=max;this[CACHE]=newMap();}set(k,v){constmap=this[CACHE];if(map.has(k))map.delete(k);map.set(k,v);// 超量淘汰if(map.size>this[MAX]){constfirstKey=map.keys().next().value;map.delete(firstKey);}}get(k){constv=this[CACHE].get(k);if(v!==undefined){// 提到最新this[CACHE].delete(k);this[CACHE].set(k,v);}returnv;}// 外部想偷看 size?门都没有// 但可以给个只读 gettergetsize(){returnthis[CACHE].size;}}// 用法constc=newLruCache(3);c.set('a',1);c.set('b',2);console.log(JSON.stringify(c));// {},Symbol 属性被隐藏console.log(Object.keys(c));// [],for-in 也扫不到

小结
Symbol 属性不参与“常规”枚举,防君子不防小人——真要用Reflect.ownKeys还是能扒光。


内置 Symbol 那些隐藏彩蛋

1. 让对象“可迭代”:Symbol.iterator

// 写一个能直接 for-of 的 RangeclassRange{constructor(start,end){this.start=start;this.end=end;}[Symbol.iterator](){letcur=this.start;constend=this.end;return{next(){if(cur<=end){return{value:cur++,done:false};}return{done:true};}};}}// 用法for(constnofnewRange(3,6)){console.log(n);// 3 4 5 6}

2. 自定义 toString:Symbol.toStringTag

classCustomCollection{get[Symbol.toStringTag](){return'MyBadassMap';}}console.log(Object.prototype.toString.call(newCustomCollection()));// [object MyBadassMap]

3. 自动拆箱:Symbol.toPrimitive

classTemperature{constructor(c){this.c=c;}[Symbol.toPrimitive](hint){if(hint==='number')returnthis.c;if(hint==='string')return`${this.c}°C`;returnthis.c;}}constt=newTemperature(25);console.log(t+0);// 25console.log(`${t}`);// 25°C

Symbol 在真实项目里能干点啥

场景 1:Redux ActionType 防冲突

// actionTypes.jsconstscope=Symbol.for('moduleA');exportconstADD=`${scope}/ADD`;exportconstREMOVE=`${scope}/REMOVE`;// 编译后仍是字符串,但 Symbol.for 保证全局唯一

场景 2:插件扩展点

// pluginSystem.jsconstHOOKS={BEFORE_CREATE:Symbol('beforeCreate'),AFTER_CREATE:Symbol('afterCreate')};exportfunctioncreatePlugin(handlers){return{[HOOKS.BEFORE_CREATE]:handlers.beforeCreate||(()=>{}),[HOOKS.AFTER_CREATE]:handlers.afterCreate||(()=>{})};}// 核心逻辑exportfunctioncreateInstance(plugin){plugin[HOOKS.BEFORE_CREATE]?.();constinst={name:'instance'};plugin[HOOKS.AFTER_CREATE]?.(inst);returninst;}

场景 3:私有方法“伪实现”

const_validate=Symbol('validate');classForm{[_validate](data){returndata.name&&data.age>0;}submit(data){if(!this[_validate](data)){thrownewError('老铁,数据不对');}console.log('提交成功');}}

踩过的坑比写过的代码还多

现场翻车逃生指南
JSON.stringify 直接吃掉 Symbol 属性JSON.stringify({a: 1, [Symbol('s')]: 2}){}Reflect.ownKeys手动序列化
for-in / Object.keys 找不到 Symbol遍历半天少字段Object.getOwnPropertySymbols兜底
跨 iframe Symbol 不相等Symbol('a') === Symbol('a')本来就是 false,但Symbol.for('a')在俩 iframe 里却是同一个想清楚再用for
TypeScript 类型报错const s: unique symbol = Symbol()必须加unique集中放symbols.ts统一导出

调试 Symbol 时的骚操作

constobj={name:'kimi',[Symbol('age')]:18,[Symbol.for('sex')]:'male'};// 1. 快速看全部 Symbolconsole.log(Object.getOwnPropertySymbols(obj));// [ Symbol(age), Symbol(sex) ]// 2. 连字符串带 Symbol 一起薅console.log(Reflect.ownKeys(obj));// [ 'name', Symbol(age), Symbol(sex) ]// 3. 打断点想单步?直接 console.tableconsole.table(Reflect.ownKeys(obj).map(k=>[k,obj[k]]));

让 Symbol 更好用的小技巧

1. 工厂函数封装

// symbols.tsexportconstcreateSymbol=(()=>{constcache=newMap<string,symbol>();return(desc:string)=>{if(!cache.has(desc))cache.set(desc,Symbol(desc));returncache.get(desc)!;};})();// 用法exportconstSIDEBAR_TOGGLE=createSymbol('layout/sidebar/toggle');

2. TypeScript 声明不闹心

declareconst__brand:uniquesymbol;typeBrand<T,B>=T&{[__brand]:B};typeUserId=Brand<string,'UserId'>;typeOrderId=Brand<string,'OrderId'>;functionqueryUser(id:UserId){/* */}queryUser('123'asUserId);// 必须强转,防串号

3. 团队协作“符号常量”管理

// @company/shared-symbolsexportconstSY={FORM_VALIDATE:Symbol.for('@company/form/validate'),TABLE_ROW:Symbol.for('@company/table/row'),CHART_TOOLTIP:Symbol.for('@company/chart/tooltip')}asconst;// 使用import{SY}from'@company/shared-symbols';// 全局唯一,不怕撞车,还能 keyFor 反查

你以为 Symbol 冷门?其实框架早用烂了

  • Vue 3reactive内部用Symbol(__v_raw)标记原始对象,防止循环代理。
  • Redux ToolkitcreateSlice里用Symbol.for('rtk-action')做 action 标识,保证不会和用户的 type 冲突。
  • Ant Design Vuetable组件,用Symbol.for('table.row')当 row 插槽的 key,避免用户自定义字段把内部字段覆盖掉。

一句话:Symbol 就像空气,平时感觉不到,缺了它框架就得窒息。


结语:
下次再看到Symbol('__whatever__'),别急着删,先 git blame 看看是谁写的,再去请他喝杯奶茶——他大概率帮你挡过一颗子弹。
好了,键盘冒烟了,我去加钟写需求了。祝你编码愉快,Symbol 用爽!

欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!


专栏系列(点击解锁)学习路线(点击解锁)知识定位
《微信小程序相关博客》持续更新中~结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
《AIGC相关博客》持续更新中~AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
《HTML网站开发相关》《前端基础入门三大核心之html相关博客》前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
《前端基础入门三大核心之JS相关博客》前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。
通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
《前端基础入门三大核心之CSS相关博客》介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
《canvas绘图相关博客》Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
《Vue实战相关博客》持续更新中~详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
《python相关博客》持续更新中~Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
《sql数据库相关博客》持续更新中~SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
《算法系列相关博客》持续更新中~算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
《IT信息技术相关博客》持续更新中~作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
《信息化人员基础技能知识相关博客》无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
《信息化技能面试宝典相关博客》涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
《前端开发习惯与小技巧相关博客》持续更新中~罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
《photoshop相关博客》持续更新中~基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
日常开发&办公&生产【实用工具】分享相关博客》持续更新中~分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询