前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库enumerable 与属性遍历
JSJavaScript对象与原型

enumerable 如何影响 for...in 和 Object.keys?

在 enumerable: false 时,for...in 与 Object.keys() 都不会输出该属性;但前者会额外遍历原型链上的可枚举属性,后者只返回对象自身的可枚举字符串属性。

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

enumerable 决定属性能否被枚举

  1. 枚举标志这些 API 还会按自有性和键类型筛选
  2. 默认值defineProperty 新增默认 false,赋值默认 true
  3. 检测方式propertyIsEnumerable 或读取属性描述符

不可枚举不等于私有,也不是读取权限控制。

核心回答

先记住这个答案

enumerable 决定属性能否参加按可枚举性筛选的操作。Object.keys() 返回自身的可枚举字符串键;for...in 还考虑原型链上的字符串键,但同名键会受到遮蔽规则影响。两者都不枚举 Symbol 键。用 Object.defineProperty() 新增属性而省略 enumerable,默认不可枚举;对普通对象通过赋值新增的普通数据属性,通常可枚举。

  • enumerable:false 对两个 API 都隐藏
  • for...in 会包含原型链可枚举属性
  • Object.keys 只取自身可枚举键

两个枚举 API 各自的过滤规则

Object.keys() 的筛选条件有三项:对象自身、字符串键、可枚举。for...in 沿原型链考察字符串键,跳过不可枚举项,并避免重复输出同名键。一个自身的不可枚举同名属性,也可能遮蔽原型上的可枚举属性,因此不能把它理解为简单拼接每一层的 Object.keys()。

默认值的差异也很关键:Object.defineProperty(obj, 'key', { value: 1 }) 新增属性且不写 enumerable 时会得到 false;而普通赋值 obj.key = 1 会得到 true。另一个区别是,for...in 会输出原型链上可枚举的继承属性,Object.keys() 不会。可通过 Object.getOwnPropertyDescriptor(obj, 'key').enumerable 查看实际状态。

验证 enumerable 与原型链影响JavaScript
const obj = {};
Object.defineProperty(obj, 'hidden', {
  value: 1,
  enumerable: false,
  configurable: true,
  writable: true
});
obj.visible = 2;
const proto = { inherited: 3 };
Object.setPrototypeOf(obj, proto);

console.log('keys:', Object.keys(obj).sort().join(','));
const forInKeys = [];
for (const key in obj) forInKeys.push(key);
console.log('for-in:', forInKeys.sort().join(','));
console.log('names:', Object.getOwnPropertyNames(obj).sort().join(','));
console.log('hidden enumerable:', Object.getOwnPropertyDescriptor(obj, 'hidden').enumerable);
查看输出与解释
keys: visible
for-in: inherited,visible
names: hidden,visible
hidden enumerable: false

hidden 不可枚举,两个遍历都不输出它;for...in 多出继承的 inherited。Object.getOwnPropertyNames() 列出全部自身字符串键,因此能看到 hidden;它不返回 Symbol 键,也不返回继承键。

遍历表单字段时内部标记被意外忽略

假设渲染一个订单对象:业务金额、币种用普通赋值产生,另用 Object.defineProperty 写入一个 enumerable:false 的 _debugTag 供内部排查。若函数用 for...in 收集字段,对象原型上某个全局可枚举辅助方法也会进入列表;而 _debugTag 无论 for...in 还是 Object.keys 都不出现,因此真正的风险不是隐藏字段被输出,而是原型字段被当成自身字段。

改用 Object.keys(order) 可以避免误把继承字段当作自身字段,但它本身不能防止原型污染,也不能充当业务字段白名单。对外只允许输出金额和币种时,应明确选取这些字段。排查隐藏属性可使用描述符或 Object.prototype.propertyIsEnumerable.call(order, "_debugTag"),避免对象覆盖同名方法。

结论在哪些条件下会失效或需要修正

普通赋值新加的属性默认 enumerable:true,想隐藏必须显式用 Object.defineProperty 设置 enumerable:false。对已存在的属性,新描述符没有写的特性会保留旧值,所以只传 value 不会把原本可枚举的属性藏起来;只有从未定义过的新属性才落入不可枚举默认。

如果对象已被冻结或密封,隐藏状态不会在运行时被外部描述符改写,但新增属性也会因不可扩展而失败,因此隐藏属性必须在冻结前定义好。对原型方法,ES6 class 中的方法默认不可枚举,不会进入 for...in;但若用户用赋值方式扩展原型,对应方法就会变成可枚举,这时 Object.keys 仍不受影响。

回答前,多想一步

容易答错的地方

把 for...in 看成只遍历对象自身
for...in 会沿原型链读取可枚举属性,所以原型上的新增属性也会被输出。若只观察对象自身的普通字段,很容易误判来源。判断是否为自身属性应使用 Object.prototype.hasOwnProperty.call(obj, key)。
把新增属性当成默认可枚举
Object.defineProperty 不传 enumerable 时,新属性的默认值是 false,不是普通赋值那样的 true。因此刚添加的属性不会出现在 Object.keys() 或 for...in 中,需要显式设置 enumerable:true 才会出现。
试着用自己的话回答

面试官还会怎么问?

enumerable:false 的属性还能用 `in` 运算符检测吗?

能。in 检查的是属性是否存在,不关心是否可枚举。要查值可用直接访问或 Reflect.get;要查描述符再用 Object.getOwnPropertyDescriptor。枚举性与存在性是两件独立的事。

Object.assign 和展开运算符会复制不可枚举属性吗?

都不会复制不可枚举属性,但它们会复制自身的可枚举字符串键和 Symbol 键,这一点与只返回字符串键的 Object.keys() 不同。复制普通对象的全部自有属性描述符可使用 Object.getOwnPropertyDescriptors() 配合 Object.defineProperties();这不是通用深拷贝,也不会复制所有内置对象的内部状态。

for...in 遍历到原型属性后应如何过滤?

在循环体内用 Object.prototype.hasOwnProperty.call(obj, key) 检查。若明确只处理自身属性,更简洁的方案是直接用 Object.keys,它不会包含原型链上的任何键。

从一道题,走向一组知识

把知识连起来

相等比较

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

属性看起来相同并不意味着对象身份相同,继续区分字段检查与引用比较。

函数与作用域

JavaScript 中 arguments 对象与函数参数(parameters)有何区别?

arguments 是类数组对象,遍历它的索引与读取 length 需要区分属性特性。

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

参考资料

  • Object.defineProperty()

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

本题目录
  1. 先记住这个答案
  2. 两个枚举 API 各自的过滤规则
  3. 遍历表单字段时内部标记被意外忽略
  4. 结论在哪些条件下会失效或需要修正
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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