前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库enumerable 与属性遍历
JavaScript对象与原型

属性设成 enumerable:false 后,for...in 和 Object.keys 还能遍历到吗?

都遍历不到。enumerable: false 的属性对 for...in 和 Object.keys 都是隐身的。两者的区别在别处:for...in 还会接着遍历原型链上的可枚举属性,Object.keys 只看对象自己的。

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

`enumerable` 只影响「被列出来」

  1. enumerable 标志决定属性会不会出现在遍历、JSON.stringify、展开运算符的结果里
  2. 默认值普通赋值创建的是 true,defineProperty 没写时是 false
  3. 检测方式obj.propertyIsEnumerable(key) 或读 Object.getOwnPropertyDescriptor

不可枚举不等于私有。属性照样能被直接读取、修改,in 和 Object.hasOwn 也都能查到它。

核心回答

先记住这个答案

两个都遍历不到。属性的 enumerable 为 false 时,for...in 和 Object.keys 都会跳过它。它们真正的区别是查找范围:for...in 先列自身的可枚举属性,再沿原型链往上列继承来的可枚举属性;Object.keys 只返回对象自身的可枚举字符串键。不可枚举属性并没有被藏起来,直接读 obj.hidden 照样能拿到值。

  • 不可枚举键不会进入常规键遍历
  • for...in 会带上原型链上的可枚举属性
  • defineProperty 新增属性默认不可枚举
  • 可用 getOwnPropertyNames 查自有键

把几种 API 放在一起对比

造一个对象:原型上有一个属性,自身有一个普通属性和一个用 defineProperty 定义的属性,然后用不同方式去列它的键。

可枚举、不可枚举、继承属性JavaScript
const proto = { inherited: 3 }
const obj = Object.create(proto)
obj.visible = 2 // 普通赋值,enumerable 默认 true
Object.defineProperty(obj, 'hidden', { value: 1 }) // 没写 enumerable,默认 false

console.log(Object.keys(obj).join(','))
const forIn = []
for (const k in obj) forIn.push(k)
console.log(forIn.join(','))
console.log(Object.getOwnPropertyNames(obj).join(','))
console.log(JSON.stringify(obj))
console.log(obj.hidden, 'hidden' in obj) // 读得到,in 也能判断出来
console.log(obj.propertyIsEnumerable('hidden'))
查看输出与解释
visible
visible,inherited
visible,hidden
{"visible":2}
1 true
false

逐行看:Object.keys 只有 visible;for...in 多了原型上的 inherited,但 hidden 依然没有;Object.getOwnPropertyNames 不管可不可枚举,把自身的字符串键全列出来;JSON.stringify 和 Object.keys 一样只认自身可枚举属性。最后两行说明,不可枚举只影响「列出来」,不影响「读得到」。

为什么内置方法遍历不出来

enumerable 最常见的作用,就是让内置方法不出现在遍历里。Array.prototype.push、Object.prototype.toString 都是不可枚举的。如果它们可枚举,每次 for...in 一个对象都会把 toString、hasOwnProperty 这一堆方法带出来。

所以给原型加方法时也要注意。直接写 Array.prototype.last = function () {},这个方法是可枚举的,之后所有数组用 for...in 遍历都会多出一个 last。正确做法是用 Object.defineProperty 定义,让它默认不可枚举。

class 里定义的方法也是不可枚举的,所以 Object.keys(new Foo()) 里看不到原型方法;但构造函数里 this.x = 1 赋的实例属性是可枚举的。

其它会跳过不可枚举属性的操作
展开运算符 { ...obj } 和 Object.assign 只复制自身的可枚举属性,不可枚举的会丢。所以用它们浅拷贝对象时,defineProperty 定义的属性会消失。Object.entries、Object.values 和 Object.keys 规则一样。
回答前,多想一步

容易答错的地方

以为 for...in 能遍历到不可枚举属性
for...in 范围更大是因为它走原型链,不是因为它能看到不可枚举属性。它和 Object.keys 一样只认 enumerable: true 的。
以为不可枚举就是私有
它只影响遍历类 API。obj.hidden 能读,writable 为 true 时也能改,in 和 Object.hasOwn 都返回 true。真正的私有要用 class 的 #field。
忘了 defineProperty 的默认值
Object.defineProperty(obj, 'x', { value: 1 }) 没写 enumerable,它就是 false,同时 writable、configurable 也都是 false。很多「属性怎么拷贝不过去」的问题就出在这里。
试着用自己的话回答

面试官还会怎么问?

怎么拿到对象所有的自有键,包括不可枚举和 Symbol?

用 Reflect.ownKeys(obj)。它返回全部自有键,包括不可枚举的字符串键和 Symbol 键。只要字符串键用 Object.getOwnPropertyNames,只要 Symbol 键用 Object.getOwnPropertySymbols。

for...in 怎么只遍历自身属性?

在循环里加一句 if (!Object.hasOwn(obj, k)) continue,把继承来的键过滤掉。更简单的办法是直接改用 Object.keys(obj).forEach(...) 或 for...of Object.entries(obj)。

想完整复制一个对象,连不可枚举属性和 getter 都保留,怎么写?

Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))。它把每个属性的描述符原样复制过去,enumerable、writable、getter 和 setter 都不会丢,原型也保持一致。

从一道题,走向一组知识

把知识连起来

相等比较

Object.is、=== 与 SameValueZero 的差别

判断两个对象「看起来一样」时常靠遍历键逐个比较,不可枚举的键会被漏掉,这和 Object.is 比较引用是两回事。

函数与作用域

arguments 和函数的形参是一回事吗?改了一个另一个会变吗?

arguments.length 就是不可枚举属性,所以 Object.keys(arguments) 只列出下标。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Object.defineProperty()

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

本题目录
  1. 先记住这个答案
  2. 把几种 API 放在一起对比
  3. 为什么内置方法遍历不出来
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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