当前位置: 首页 > news >正文

21-Symbol、Map 与 Set

Symbol、Map 与 Set

三种 ES6 新增的数据类型,分别解决「唯一标识」「键值对映射」和「值去重」的场景,让代码表达更精准、性能更优。


学习目标

读完本文,你将学会:

  • Symbol 的用途:创建唯一标识、定义常量、模拟私有属性
  • Map 的用法:何时用 Map 替代 Object,常用 API 与遍历方式
  • Set 的用法:去重、集合运算(交并差)
  • WeakMap 与 WeakSet 的适用场景

一、Symbol:独一无二的标识符

1.1 基本用法

Symbol 是一种新的原始数据类型,每个 Symbol 值都是唯一的。

consts1=Symbol();consts2=Symbol();console.log(s1===s2);// false

可以给 Symbol 添加描述(仅用于调试):

constid=Symbol("userId");console.log(id.description);// "userId"

1.2 用作对象属性键

Symbol 可以作为对象属性键,不会与字符串键冲突

constuser={name:"小明",[Symbol("id")]:123};// Symbol 属性不会出现在 for...in 中for(letkeyinuser){console.log(key);// 只输出 "name"}// 用 Object.keys 也拿不到console.log(Object.keys(user));// ["name"]// 需要用专门的 APIconsole.log(Object.getOwnPropertySymbols(user));// [Symbol(id)]

1.3 定义常量,避免魔法字符串

// 传统方式:容易冲突、拼写错误constSTATUS_PENDING="pending";constSTATUS_DONE="done";// Symbol 方式:绝对唯一constSTATUS={PENDING:Symbol("pending"),DONE:Symbol("done"),ERROR:Symbol("error")};functionhandle(status){switch(status){caseSTATUS.PENDING:return"加载中...";caseSTATUS.DONE:return"完成!";caseSTATUS.ERROR:return"出错了";}}

1.4 Symbol.for 与 Symbol.keyFor

Symbol.for()会在全局注册表中查找或创建 Symbol,相同 key 返回同一个 Symbol:

consta=Symbol.for("app.config");constb=Symbol.for("app.config");console.log(a===b);// true// 反向查找 keyconsole.log(Symbol.keyFor(a));// "app.config"// 普通 Symbol 不在全局注册表中constc=Symbol("local");console.log(Symbol.keyFor(c));// undefined

1.5 常用内置 Symbol(Well-Known Symbols)

JavaScript 预定义了一些 Symbol,用于控制对象行为:

Symbol作用
Symbol.iterator定义对象的默认迭代器(for…of)
Symbol.toStringTag自定义Object.prototype.toString.call()的返回值
Symbol.hasInstance自定义instanceof行为
// Symbol.toStringTag 示例classMyClass{get[Symbol.toStringTag](){return"MyClass";}}console.log(Object.prototype.toString.call(newMyClass()));// "[object MyClass]"

二、Map:比 Object 更强大的键值对

2.1 为什么需要 Map

Object 用作键值对有一些局限:

Object 的局限Map 的优势
键只能是字符串或 Symbol键可以是任意类型(对象、函数、NaN 等)
没有直接获取大小的方法.size属性
遍历顺序不保证插入顺序遍历
原型链可能带来意外属性纯净,无原型链干扰

2.2 基本用法

constmap=newMap();// 设置键值对map.set("name","小明");map.set(123,"数字键");map.set({id:1},"对象键");// 获取值console.log(map.get("name"));// "小明"console.log(map.get(123));// "数字键"// 检查是否存在console.log(map.has("name"));// true// 删除map.delete(123);// 大小console.log(map.size);// 2// 清空map.clear();

2.3 初始化与迭代

// 用数组初始化constuserMap=newMap([["name","小明"],["age",18],["city","北京"]]);// 遍历键值对for(const[key,value]ofuserMap){console.log(`${key}:${value}`);}// 只遍历键for(constkeyofuserMap.keys()){console.log(key);}// 只遍历值for(constvalueofuserMap.values()){console.log(value);}// forEachuserMap.forEach((value,key)=>{console.log(`${key}=${value}`);});

2.4 Map 与 Object 互转

constmap=newMap([["a",1],["b",2]]);// Map → Objectconstobj=Object.fromEntries(map);console.log(obj);// { a: 1, b: 2 }// Object → Mapconstmap2=newMap(Object.entries(obj));console.log(map2);// Map { 'a' => 1, 'b' => 2 }

2.5 实用场景

// 用对象做键(缓存场景)constcache=newMap();functionfetchData(user){if(cache.has(user)){returncache.get(user);// 命中缓存}constdata={/* 请求数据 */};cache.set(user,data);returndata;}constuser1={id:1};fetchData(user1);// 首次请求fetchData(user1);// 命中缓存

三、Set:自动去重的值集合

3.1 基本用法

Set 是值的集合,每个值只能出现一次。

constset=newSet();set.add(1);set.add(2);set.add(2);// 重复,被忽略set.add(3);console.log(set.size);// 3console.log(set.has(2));// trueset.delete(2);console.log([...set]);// [1, 3]

3.2 最常用:数组去重

constnumbers=[1,2,2,3,3,3,4];constunique=[...newSet(numbers)];console.log(unique);// [1, 2, 3, 4]// 字符串去重constchars=[...newSet("hello")];console.log(chars);// ['h', 'e', 'l', 'o']

3.3 集合运算

Set 本身没有内置交并差方法,但可以用扩展运算符实现:

consta=newSet([1,2,3]);constb=newSet([2,3,4]);// 并集constunion=newSet([...a,...b]);console.log([...union]);// [1, 2, 3, 4]// 交集constintersection=newSet([...a].filter(x=>b.has(x)));console.log([...intersection]);// [2, 3]// 差集(a 有但 b 没有)constdifference=newSet([...a].filter(x=>!b.has(x)));console.log([...difference]);// [1]

3.4 遍历 Set

constset=newSet(["red","green","blue"]);// for...offor(constcolorofset){console.log(color);}// forEach(注意:Set 的 forEach 参数是 value, value, set)set.forEach((value)=>{console.log(value);});

四、WeakMap 与 WeakSet

4.1 WeakMap

WeakMap 与 Map 类似,但有两个关键区别:

  1. 键必须是对象(不能是原始值)
  2. 键是弱引用,不阻止垃圾回收
letuser={name:"小明"};constweakMap=newWeakMap();weakMap.set(user,"额外数据");// 当 user 不再被其他地方引用时user=null;// weakMap 中的条目会被自动回收

用途:给对象附加私有数据,而不影响其生命周期。

constprivateData=newWeakMap();classUser{constructor(name){privateData.set(this,{password:"secret"});this.name=name;}getPassword(){returnprivateData.get(this).password;}}

4.2 WeakSet

WeakSet 只能存储对象,同样是弱引用:

constvisited=newWeakSet();functionprocess(obj){if(visited.has(obj))return;// 已处理过visited.add(obj);// 处理逻辑...}

WeakMap/WeakSet 的限制

  • 没有.size属性
  • 不能遍历(keys/values/entries/forEach 都没有)
  • 不能清除(没有.clear()

五、Map vs Object,Set vs Array

Map vs Object 怎么选?

场景推荐
键需要是对象/函数Map
频繁增删键值对Map
需要保持插入顺序Map
需要知道大小Map
简单的配置对象、JSON 数据Object
需要 JSON 序列化Object(Map 不能直接 JSON.stringify)

Set vs Array 怎么选?

场景推荐
需要去重Set
频繁检查元素是否存在Set(O(1))
需要有序列表、按索引访问Array
需要 map/filter/reduceArray

六、常见误区与注意点

误区正确理解
Symbol 可以用 new 创建new Symbol()会报错,用Symbol()
Symbol.for("x") === Symbol("x")Symbol.forSymbol创建的是不同的 Symbol
Set 去重对对象有效{a:1}{a:1}是不同的对象,都能放入 Set
Map 可以用.语法访问Map 用.get().set(),不是.map.key
WeakMap 可以遍历WeakMap 没有迭代方法,不可遍历
typeof Symbol()是 “object”typeof Symbol()是 “symbol”

Symbol 属性不可枚举的完整含义

constobj={name:"小明",[Symbol("id")]:123};console.log(JSON.stringify(obj));// '{"name":"小明"}'console.log(Object.keys(obj));// ["name"]console.log(Object.getOwnPropertySymbols(obj));// [Symbol(id)]

七、动手练习

练习 1:用 Symbol 实现枚举

用 Symbol 定义一组颜色常量,避免字符串常量冲突:

constCOLOR={/* ... */};functiongetColorHex(color){// 返回对应颜色的十六进制值}
参考答案
constCOLOR={RED:Symbol("red"),GREEN:Symbol("green"),BLUE:Symbol("blue")};functiongetColorHex(color){constmap={[COLOR.RED]:"#ff0000",[COLOR.GREEN]:"#00ff00",[COLOR.BLUE]:"#0000ff"};returnmap[color]||"#000000";}console.log(getColorHex(COLOR.RED));// "#ff0000"

练习 2:Map 统计字符频率

输入一个字符串,用 Map 统计每个字符出现的次数:

functioncountChars(str){// 返回一个 Map,key 是字符,value 是次数}console.log(countChars("hello"));// Map { 'h' => 1, 'e' => 1, 'l' => 2, 'o' => 1 }
参考答案
functioncountChars(str){constmap=newMap();for(constcharofstr){map.set(char,(map.get(char)||0)+1);}returnmap;}

练习 3:Set 实现数组交集

写一个不依赖扩展运算符的数组交集函数:

functionintersection(arr1,arr2){// 返回两个数组的交集(去重)}console.log(intersection([1,2,2,3],[2,3,4]));// [2, 3]
参考答案
functionintersection(arr1,arr2){constset2=newSet(arr2);constresult=newSet();for(constitemofarr1){if(set2.has(item))result.add(item);}return[...result];}

八、AI 辅助学习

8.1 本节知识点的 AI 提问模板

【背景】我是 JavaScript 初学者,正在学习第 21 篇"Symbol、Map 与 Set"。 我已经了解 Object 和 Array 的基本用法。 【问题】我理解 Map 可以替代 Object 做键值对存储,但我不太清楚什么时候 应该优先选择 Map。在 React/Vue 这样的框架中,哪些场景更适合用 Map? 【期望】请对比 Map 和 Object 在以下场景的表现:键类型多样性、迭代性能、 内存占用、JSON 序列化。给出 2 个前端开发中适合用 Map 的具体例子。

8.2 用 AI 验证你的理解

  • 问 AI:“typeof Symbol('x')的结果是什么?typeof new Map()呢?”
  • 让 AI 解释:“为什么 WeakMap 的键必须是对象?”
  • 让 AI 出题:“写一道 Set 去重和 filter 去重的性能对比题”

8.3 警惕 AI 的常见错误

  • AI 可能写出new Symbol()(正确是Symbol()
  • AI 可能声称 Map 的键是无序的(实际按插入顺序)
  • AI 可能在 WeakMap 中使用原始值作为键(会报错)
  • AI 可能混淆Symbol.forSymbol的区别

九、配套代码

本文示例代码位于:CODE/21-Symbol-Map-Set/

文件名说明
data-structures-lab.htmlSymbol、Map、Set 交互式实验室

十、本章小结

  • Symbol:唯一标识符,适合枚举、私有属性,用Symbol.for共享
  • Map:键可为任意类型,按插入顺序,有.size,支持任意键对象
  • Set:值不重复,最常用场景是数组/字符串去重
  • WeakMap/WeakSet:弱引用,键必须是对象,不可遍历,适合附加元数据
  • 选择建议:需要任意键用 Map,需要去重用 Set,需要不影响 GC 用 WeakMap

十一、下篇预告

下一篇学习面向对象新语法:《类(Class):面向对象的新写法》,你将学到:

  • class 语法糖与构造函数的对比
  • 继承 extends 和方法重写
  • 静态属性和私有字段
  • getter/setter 的优雅写法

如果本文对你有帮助,欢迎点赞、收藏、关注专栏。有任何问题可以在评论区交流!

http://www.jsqmd.com/news/1061202/

相关文章:

  • 2026深圳高端烫染发型师测评:谁更适合长期固定 - 魔力阿布
  • 2026年河南中小企业AI搜索获客完全指南:如何选择靠谱的GEO优化服务商 - 优质企业观察收录
  • Web自动化测试弹窗处理:策略、实践与装饰器模式应用
  • GEO代理哪家好?哪些公司支持代理GEO?2026年GEO代理营销服务商怎么选?这五家专业服务商值得推荐 - 互联网科技品牌测评
  • PowerPC e600缓存一致性实战:从MESI协议到多核调试避坑指南
  • CentOS 8 搭建符合 RFC 5280 的三级 PKI 证书体系
  • 2026烫钻机源头工厂清单:浙江烫钻机工厂-富知源烫钻机,厂家直供 - 栗子测评
  • 2026榆林空调维修公司排名|本地口碑好的正规上门平台推荐 - 邻家快修
  • 2026年河南AI搜索推广与GEO优化服务商深度评测:企业精准获客完全指南 - 优质企业观察收录
  • 猫抓Cat-Catch技术解析:现代浏览器资源嗅探的三大核心架构与实战应用
  • 2026年河南企业AI搜索推广怎么选?GEO优化服务商深度横评与避坑指南 - 优质企业观察收录
  • 2026年河南中小企业AI搜索推广服务商深度横评:从GEO优化到精准获客的完整指南 - 优质企业观察收录
  • 2026年 广东机器人钛制品厂家/钛配件/钛制品加工推荐榜:精密定制与创新工艺实力解析 - 品牌发掘
  • 2026年河南中小企业AI搜索推广与GEO优化服务商完全选型指南 - 优质企业观察收录
  • MonkeyCode遗留系统改造:AI助力老代码现代化
  • ComfyUI-SUPIR:AI智能图像超分辨率修复技术深度解析
  • 2026上饶空调维修公司排名|本地口碑好的正规上门平台推荐 - 邻家快修
  • 2026年开封AI搜索优化服务商全景评测:豆包、DeepSeek精准获客方案对比 - 优质企业观察收录
  • 2026优质全自动摇钻机生产厂家推荐:专业刷钻机厂家+摇钻机制造商供货 - 栗子测评
  • 2026青岛门窗本地口碑推荐:老业主亲测好用的五大实力品牌 - 哦第一
  • EVB51JM128评估板USB开发实战:从环境搭建到协议栈应用
  • 青岛同城头部繁育探店实测 朋博猫舍犬舍综合实力本地排名第一 - 同城宠物优选基地
  • ANNAFENDI:百年意式奢感,解锁日常低调轻奢新风尚 - 每日行业榜
  • EVBUSB2SER评估板:USB转串口硬件配置、驱动安装与通信调试全指南
  • 从0到1做短视频配音,2026年这6款免费软件按阶段推荐,少走3年弯路 - AI测评
  • 武汉叠拼别墅装修公司实测盘点:谁在真正懂大宅? - 品牌红黑榜
  • 基于Robot Framework构建Web+APP+接口一体化自动化测试平台实战
  • 2026年河南中小企业AI搜索优化(GEO)服务商选型指南:本地获客方案对标分析 - 优质企业观察收录
  • Seedance 2.0:漫剧工业化工作流的AI叙事操作系统
  • 2026年河南中小企业AI搜索推广完全指南:GEO优化如何助力开封、郑州企业实现精准获客 - 优质企业观察收录