前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Symbol 属性键 字符串键
JavaScript值与类型

用 Symbol 当对象的键,和字符串键有什么不同?哪些 API 会忽略它?

Symbol 键每个都独一无二,不会和别人的字符串键撞名。Object.keys、for...in、JSON.stringify 都会跳过它,要读出来得用 Object.getOwnPropertySymbols 或 Reflect.ownKeys。它只是不显眼,并不私有。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#值与类型
先看核心答案读代码示例
理解线索

Symbol 键的可见范围

  1. 被忽略Object.keys、Object.entries、for...in、JSON.stringify 只处理字符串键
  2. 能读到Object.getOwnPropertySymbols 只返回 Symbol 键,Reflect.ownKeys 两种都返回
  3. 会被复制{ ...obj } 和 Object.assign 会复制自身可枚举的 Symbol 键

很多人以为 Symbol 键是私有属性,其实任何人拿到对象都能用 Object.getOwnPropertySymbols 把它读出来。真正的私有要用 class 的 # 私有字段或闭包。

核心回答

先记住这个答案

对象的键只有两种类型:字符串和 Symbol。字符串键谁都能写出同一个名字,Symbol 键却是每次 Symbol() 新生成的唯一值,所以不会和别的库、别的代码加的属性冲突。代价是它比较「低调」:Object.keys、Object.entries、for...in 和 JSON.stringify 都会跳过 Symbol 键。想拿到它,用 Object.getOwnPropertySymbols 单独取,或者用 Reflect.ownKeys 一次取出全部自有键。

  • Symbol 键不会和任何字符串键冲突
  • 常规字符串键枚举跳过 Symbol 键
  • Reflect.ownKeys 可获取 Symbol 键
  • 展开与 Object.assign 可复制符号键

哪些 API 看得见 Symbol 键

最直接的办法是给一个对象同时放字符串键和 Symbol 键,然后挨个 API 试一遍。

同一个对象,不同 API 的结果JavaScript
const id = Symbol('id')
const user = { name: 'Tom', [id]: 42 }
// Symbol 不能隐式转字符串,打印前先用 String() 显式转换
const show = (keys) => keys.map(String).join(', ')
// 这些 API 只看字符串键,Symbol 键被跳过
console.log(show(Object.keys(user)), JSON.stringify(user))
for (const key in user) console.log('for...in', key)
// 专门拿 Symbol 键,或者全都要
console.log(show(Object.getOwnPropertySymbols(user)))
console.log(show(Reflect.ownKeys(user)))
// 展开和 Object.assign 会复制可枚举的 Symbol 键
console.log({ ...user }[id], Object.assign({}, user)[id])
// 访问必须用同一个 Symbol,字符串 'id' 访问不到
console.log(user.id, user[id])
查看输出与解释
name {"name":"Tom"}
for...in name
Symbol(id)
name, Symbol(id)
42 42
undefined 42

user.id 是 undefined,因为点语法访问的是字符串键 'id',和 Symbol 键是两回事。要读 Symbol 键只能用方括号,并且手里得有当初那个 Symbol。 展开运算符会复制 Symbol 键,这点经常被答错。{ ...obj } 和 Object.assign 复制的是「自身的、可枚举的」属性,不区分键的类型。但 JSON.parse(JSON.stringify(obj)) 这种深拷贝会把 Symbol 键丢掉。

实际项目里拿它做什么

第一个场景是给别人的对象挂数据又不想撞名。比如在第三方库返回的对象上缓存一个计算结果,用字符串 __cache 有可能和库内部的字段重名,用 Symbol 就不会,而且序列化、遍历时也不会把这个缓存带出去。

第二个场景是语言内置的「钩子」。Symbol.iterator 让对象能被 for...of 遍历,Symbol.toPrimitive 控制对象的类型转换,Symbol.toStringTag 改变 Object.prototype.toString 的输出。这些协议都用 Symbol 做键,就是为了不和用户已有的字符串属性冲突,老代码里叫 iterator 的属性也不会被误认成迭代器。

回答前,多想一步

容易答错的地方

Symbol 键就是私有属性
不是。Object.getOwnPropertySymbols 和 Reflect.ownKeys 能把所有 Symbol 键列出来,拿到键就能读写。它只是不会在常规遍历里出现。需要真正私有,用 class 的 #field 或者闭包。
展开运算符会丢掉 Symbol 键
不会。{ ...obj } 和 Object.assign 都会复制可枚举的 Symbol 键。会丢的是 JSON.stringify,以及只遍历 Object.keys 自己手写的拷贝函数。
Object.getOwnPropertyNames 能拿到所有键
它能拿到不可枚举的字符串键,但同样不包含 Symbol 键。要一次拿到全部,只有 Reflect.ownKeys。
试着用自己的话回答

面试官还会怎么问?

用 Symbol.for 创建的键也会被这些 API 忽略吗?

会。Symbol.for('id') 只是从全局注册表里取同一个 Symbol,类型还是 Symbol,Object.keys 照样跳过它。区别只在于任何地方调用 Symbol.for('id') 都能拿到同一个键,也就失去了「别人猜不到」的效果。

手写深拷贝时怎么保留 Symbol 键?

遍历键时用 Reflect.ownKeys(obj) 代替 Object.keys(obj),这样字符串键和 Symbol 键都能拿到。如果还要保留不可枚举属性和 getter,可以配合 Object.getOwnPropertyDescriptors 和 Object.defineProperties 一起用。

Reflect.ownKeys 返回的键是什么顺序?

先是整数形式的键,按数值从小到大;再是其它字符串键,按添加顺序;最后是 Symbol 键,也按添加顺序。所以 Symbol 键永远排在所有字符串键后面。

从一道题,走向一组知识

把知识连起来

值与类型

为什么 Symbol() 每次都不相等,还不能用 new 调用?

先弄清 Symbol() 为什么每次都返回新值,才明白 Symbol 键为什么不会撞名。

值与类型

模板字符串和加号拼接,类型转换有什么不一样?嵌套时要注意什么?

把 Symbol 放进模板字符串会直接抛 TypeError,是 Symbol 不能隐式转字符串的另一个表现。

参考资料

  • JavaScript:值与类型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 哪些 API 看得见 Symbol 键
  3. 实际项目里拿它做什么
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑