前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库Object.keys getOwnPropertyNames getOwnPropertySymbols 区别
JavaScript对象与原型

Object.keys、getOwnPropertyNames、getOwnPropertySymbols 各能拿到哪些键?

三个方法都只看对象自己的属性,不管原型链。区别在两个维度:Object.keys 只要可枚举的字符串键;Object.getOwnPropertyNames 要全部字符串键,不可枚举的也算;Object.getOwnPropertySymbols 只要 Symbol 键,不管是否可枚举。

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

按「键类型 × 可枚举」划分

  1. Object.keys字符串键,只要可枚举的
  2. Object.getOwnPropertyNames字符串键,可枚举和不可枚举都要
  3. Object.getOwnPropertySymbolsSymbol 键,可枚举和不可枚举都要

getOwnPropertySymbols 不看可枚举性,这和 Object.keys 的思路相反,很多人以为它也只返回可枚举的。

核心回答

先记住这个答案

三个方法都只返回对象自身的属性,原型链上的一个都不要。区别在两个维度上:键是字符串还是 Symbol,属性是否可枚举。Object.keys 只要可枚举的字符串键,Object.getOwnPropertyNames 要全部字符串键(包括不可枚举的),Object.getOwnPropertySymbols 只要 Symbol 键(不管是否可枚举)。想一次拿到自身全部的键,用 Reflect.ownKeys,它等于后两者合起来。

  • 三个方法都只看自身属性,原型链上的一律不管
  • Object.keys 只要可枚举的字符串键
  • getOwnPropertyNames 包括隐藏字符串键
  • 要一次拿全所有键用 Reflect.ownKeys

一个对象上看三个方法的差别

构造一个对象,同时带普通属性、不可枚举属性、Symbol 属性,再给它挂个原型。

对照三个方法的返回值JavaScript
const id = Symbol('id')
const user = { name: 'Tom', [id]: 42 }
Object.defineProperty(user, 'secret', { value: 'x', enumerable: false })
const admin = Object.create(user) // 原型上的属性三个方法都不管
admin.role = 'admin'

console.log(Object.keys(user).join())
console.log(Object.getOwnPropertyNames(user).join())
console.log(Object.getOwnPropertySymbols(user).map(String).join())
console.log(Reflect.ownKeys(user).map(String).join()) // 三类合在一起
console.log(Object.keys(admin).join())
查看输出与解释
name
name,secret
Symbol(id)
name,secret,Symbol(id)
role

secret 用 defineProperty 定义时 enumerable 为 false,所以 Object.keys 看不到它,getOwnPropertyNames 能看到。Symbol 键 id 两个字符串方法都不返回,只在 getOwnPropertySymbols 里出现。admin 继承了 user 的全部属性,但 Object.keys(admin) 只有它自己的 role。

不可枚举属性从哪来

平时写 obj.x = 1 或者字面量定义的属性都是可枚举的,所以不可枚举属性主要来自两个地方。

一个是语言内置的属性。数组的 length、函数的 name 和 length、class 里定义的方法都是不可枚举的。比如 Object.getOwnPropertyNames([1]) 返回 ['0', 'length'],而 Object.keys([1]) 只有 ['0']。class 方法不可枚举这一点很重要,它意味着用 Object.keys 或 Object.assign 处理 SomeClass.prototype 什么也拿不到。

另一个是用 Object.defineProperty 主动隐藏。库作者经常这样挂内部状态,比如 Vue 2 在响应式对象上挂的 __ob__ 就是不可枚举的,这样用户遍历、JSON.stringify 时都不会看到它。

Symbol 键为什么单独一个方法
Symbol 是 ES2015 才加的。当时 Object.keys 和 getOwnPropertyNames 已经被大量代码使用,如果让它们突然返回 Symbol,老代码里 key.toUpperCase() 这类字符串操作就会报错。所以标准选择新开一个方法,旧方法的行为保持不变。这也是 Symbol 适合做「不会和别人冲突的隐藏键」的原因:普通遍历根本看不到它。
回答前,多想一步

容易答错的地方

以为 Object.keys 和 for...in 一样
for...in 会遍历原型链上可枚举的字符串键,Object.keys 只看自身。上面的 admin 用 for...in 会得到 role 和 name 两个键。项目里想只遍历自身属性,直接用 Object.keys 或 Object.entries,不用在 for...in 里再加 hasOwn 过滤。
以为 getOwnPropertyNames 能拿到所有键
它名字里有 Names,只管字符串键。Symbol 键要单独用 getOwnPropertySymbols,或者直接用 Reflect.ownKeys 一次拿全。
以为三个方法的顺序是随意的
返回顺序有规则:先是整数索引键按数值升序,然后是其他字符串键按添加顺序,Symbol 键也按添加顺序,Reflect.ownKeys 里 Symbol 排在所有字符串之后。所以 { b: 1, 2: 1, a: 1, 1: 1 } 的 Object.keys 结果是 ['1', '2', 'b', 'a']。
试着用自己的话回答

面试官还会怎么问?

JSON.stringify 和展开运算符会带上哪些属性?

JSON.stringify 只处理自身可枚举的字符串键,和 Object.keys 范围一致,Symbol 键直接忽略。对象展开 { ...obj } 和 Object.assign 复制的是自身可枚举属性,字符串键和 Symbol 键都会复制。

深拷贝时要用哪个方法取键?

要保留 Symbol 键和不可枚举属性,就用 Reflect.ownKeys 取键,再用 Object.getOwnPropertyDescriptor 拿描述符,用 Object.defineProperty 写到新对象上,这样 enumerable、getter 都能原样保留。只拷贝普通数据的话 structuredClone 更省事,但它会丢掉 Symbol 键和函数。

Object.keys 传一个字符串会怎样?

ES2015 起会先把参数转成对象,Object.keys('ab') 返回 ['0', '1']。传 null 或 undefined 会抛 TypeError,因为它们没法转成对象。

从一道题,走向一组知识

把知识连起来

对象与原型

一个 class 只能继承一个父类,想复用多组方法怎么办?

用 Object.assign 做 mixin 时只会复制可枚举的自身属性,class 方法不可枚举,正好是这道题的一个反例。

对象与原型

Object.create(null) 创建的对象有什么特别?哪些方法用不了?

Object.create 指定原型后,原型上的属性三个方法都拿不到,能帮你分清「自身」和「继承」。

参考资料

  • JavaScript:对象与原型参考资料

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

本题目录
  1. 先记住这个答案
  2. 一个对象上看三个方法的差别
  3. 不可枚举属性从哪来
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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