JS遍历对象的5种实用方法
在JavaScript中,遍历对象属性是常见的操作,但选择合适的方法至关重要。本文深入探讨了JS遍历对象的几种核心方法,助你提升代码效率和避免潜在陷阱。从传统的`for...in`循环(需注意`hasOwnProperty`过滤原型链属性)到现代的`Object.keys()`、`Object.values()`和`Object.entries()`,这些方法结合`forEach`或`for...of`循环,提供了更安全、高效的遍历方式,尤其推荐在现代开发中使用。此外,针对不可枚举属性和Symbol属性,本文还介绍了`Object.getOwnPropertyNames()`和`Object.getOwnPropertySymbols()`,以及全属性遍历利器`Reflect.ownKeys()`。掌握这些方法,让你在处理各种JavaScript对象时游刃有余。
遍历JavaScript对象需根据数据结构和目的选择方法:for...in可遍历可枚举属性但需hasOwnProperty过滤原型链属性;Object.keys()、Object.values()、Object.entries()返回数组,结合forEach或for...of更安全高效,推荐用于现代开发;处理不可枚举属性用Object.getOwnPropertyNames(),Symbol属性用Object.getOwnPropertySymbols(),全属性遍历可用Reflect.ownKeys()。
在JavaScript里,遍历一个对象其实有那么几种核心方式,每种都有自己的脾气和适用场景。没有一招鲜吃遍天的,关键在于你手头的数据结构和想达成什么目的。简单来说,你需要访问对象的键、值,或者键值对,选择合适的工具能让你事半功倍,也能避免一些不必要的坑。
解决方案
要遍历JavaScript对象,我们通常会用到以下几种方法,它们各有侧重,也代表了JS发展中对对象遍历能力的不同演进:
for...in
循环: 这是最老牌的,也是最容易让人踩坑的。它会遍历对象所有可枚举的属性,包括原型链上的属性。如果你不加处理,很容易拿到一些你本不想要的属性。const myObject = { name: '张三', age: 30, city: '北京' }; for (const key in myObject) { // 关键:使用 hasOwnProperty 检查,确保只处理对象自身的属性 if (myObject.hasOwnProperty(key)) { console.log(`${key}: ${myObject[key]}`); } } // 输出: // name: 张三 // age: 30 // city: 北京
Object.keys()
结合数组方法(如forEach
或for...of
): 现代JS里,我个人更推荐这种方式。Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。拿到数组后,你就可以用各种数组方法来遍历了。const myObject = { name: '李四', age: 25, job: '工程师' }; // 使用 forEach Object.keys(myObject).forEach(key => { console.log(`${key}: ${myObject[key]}`); }); // 输出: // name: 李四 // age: 25 // job: 工程师 // 或者使用 for...of 循环 for (const key of Object.keys(myObject)) { console.log(`${key}: ${myObject[key]}`); }
Object.values()
结合数组方法: 如果你只关心对象的值,Object.values()
就很方便了。它返回一个由给定对象自身的所有可枚举属性的值组成的数组。const myObject = { a: 1, b: 'hello', c: true }; Object.values(myObject).forEach(value => { console.log(value); }); // 输出: // 1 // hello // true
Object.entries()
结合数组方法或解构: 这是我个人觉得最优雅的方式之一,尤其是当你需要同时处理键和值的时候。Object.entries()
返回一个给定对象自身可枚举属性的键值对数组,每个内部数组包含[key, value]
。const myObject = { id: 101, product: '笔记本电脑', price: 8999 }; // 使用 forEach 和数组解构 Object.entries(myObject).forEach(([key, value]) => { console.log(`${key}: ${value}`); }); // 输出: // id: 101 // product: 笔记本电脑 // price: 8999 // 或者使用 for...of 循环和数组解构 for (const [key, value] of Object.entries(myObject)) { console.log(`${key}: ${value}`); }
何时选择 for...in
遍历对象,它有什么陷阱?
for...in
循环在 JavaScript 中算是历史比较悠久的遍历方式了,它设计之初是为了枚举对象的所有可枚举属性,包括那些从原型链上继承来的属性。这听起来好像很强大,但实际上,这正是它最容易让人“翻车”的地方。
你可能会在一些老旧的代码库里看到它的身影,或者在需要遍历非数组对象(比如纯粹的键值对集合)时偶尔被用到。它的一个优点是,它能直接访问到属性名,并且在某些特定场景下,比如你需要检查一个对象的所有属性,包括继承的,它可能显得直接。
然而,它的陷阱也显而易见:
遍历原型链: 这是最常见的坑。如果你不加
hasOwnProperty()
检查,for...in
会把对象原型上的可枚举属性也遍历出来。这在很多情况下都不是你想要的,可能会导致意料之外的行为或者 bug。比如,你给Object.prototype
加了个属性,那么所有对象在for...in
时都会“无辜”地多出这个属性。Object.prototype.myCustomProp = '这是一个原型属性'; // 尽量不要这么做! const data = { name: 'Alice', age: 28 }; for (const key in data) { console.log(key, data[key]); } // 可能会输出: // name Alice // age 28 // myCustomProp 这是一个原型属性 (如果 myCustomProp 是可枚举的)
为了避免这个问题,几乎成了惯例,每次使用
for...in
都得加上if (obj.hasOwnProperty(key))
这层防护。这就像是在一个不那么安全的街区走路,你总得给自己加个保险。属性顺序不确定性(旧版本JS): 在ES5及之前的版本,
for...in
遍历属性的顺序是无法保证的,它取决于JavaScript引擎的实现。虽然现代JS引擎(ES2015+)通常会按照属性创建的顺序来遍历数字键和字符串键,但对于混合了数字和非数字键的复杂对象,或者你对顺序有严格要求时,它依然不是最可靠的选择。性能考量: 相比于
Object.keys()
配合数组方法,for...in
在某些场景下可能会略慢,因为它需要检查原型链。当然,对于小对象来说,这点差异几乎可以忽略不计。
所以,我的建议是:如果你不是在维护一些老代码,或者确实需要遍历原型链上的可枚举属性(这种场景其实很少),那么尽量避免直接使用 for...in
。现代的 Object.keys()
、Object.values()
或 Object.entries()
配合 for...of
或 forEach
循环,通常是更安全、更可预测、也更符合现代编程习惯的选择。它们直接返回数组,你可以用数组的所有强大功能来处理,心智负担也小很多。
使用 Object.keys()
、Object.values()
和 Object.entries()
遍历对象的最佳实践是什么?
当你谈到现代JavaScript中遍历对象,Object.keys()
、Object.values()
和 Object.entries()
几乎是绕不开的。它们是ES6(ES2015)引入的,提供了一种更安全、更灵活、也更“函数式”的遍历方式。我个人在写业务代码时,如果不是有特殊原因,基本都倾向于使用它们,心里踏实。
它们的“最佳实践”其实很简单,就是充分利用它们返回数组的特性,然后结合数组的各种遍历方法。
返回数组的优势: 这三者最大的优势在于它们都返回一个新数组。这意味着你不再需要担心原型链上的属性污染,也不用手动去写
hasOwnProperty
这样的检查。它们只关注对象自身的、可枚举的属性。一旦你拿到了数组,那么JavaScript数组的丰富API就为你敞开了大门。Object.keys(obj)
: 获取所有键的数组。Object.values(obj)
: 获取所有值的数组。Object.entries(obj)
: 获取所有[key, value]
对的数组。
配合
forEach
进行简单遍历: 这是最常见的用法,如果你只是想对每个键、值或键值对执行一个操作,forEach
是个不错的选择。const user = { name: 'Bob', age: 35, email: 'bob@example.com' }; // 遍历键 Object.keys(user).forEach(key => { console.log(`键: ${key}`); }); // 遍历值 Object.values(user).forEach(value => { console.log(`值: ${value}`); }); // 遍历键值对 (推荐使用数组解构) Object.entries(user).forEach(([key, value]) => { console.log(`${key} => ${value}`); });
配合
for...of
进行结构化遍历:for...of
循环是遍历可迭代对象(包括数组)的利器。它比forEach
更灵活,支持break
、continue
,并且在异步操作时也更方便。特别是配合Object.entries()
进行解构赋值,代码可读性极高。const config = { host: 'localhost', port: 8080, debug: true }; for (const key of Object.keys(config)) { console.log(`配置项: ${key}`); } for (const [key, value] of Object.entries(config)) { console.log(`配置 ${key} 是 ${value}`); if (key === 'port' && value < 1024) { console.warn('端口号可能过低!'); break; // 可以在 for...of 中中断循环 } }
配合
map
,filter
,reduce
进行数据转换和聚合: 这才是这些方法真正强大的地方。因为它们返回的是数组,你可以轻松地对数据进行转换、筛选或聚合操作,这在处理复杂数据结构时尤其有用。const products = { p1: { name: '鼠标', price: 120 }, p2: { name: '键盘', price: 300 }, p3: { name: '显示器', price: 1200 } }; // 获取所有产品名称 const productNames = Object.values(products).map(p => p.name); console.log(productNames); // ['鼠标', '键盘', '显示器'] // 找出价格低于500的产品 const affordableProducts = Object.entries(products) .filter(([id, product]) => product.price < 500) .map(([id, product]) => ({ id, name: product.name, price: product.price })); console.log(affordableProducts); // [{ id: 'p1', name: '鼠标', price: 120 }, { id: 'p2', name: '键盘', price: 300 }] // 计算总价格 const totalPrice = Object.values(products).reduce((sum, product) => sum + product.price, 0); console.log(totalPrice); // 1620
总之,这些方法让对象遍历变得更加现代化和函数式。它们返回数组的特性,使得你可以无缝地衔接数组的各种强大操作,从而写出更简洁、更安全、也更具表达力的代码。我个人觉得,除非有非常特殊的需求(比如需要遍历不可枚举属性,那就要用到更底层的 Object.getOwnPropertyNames
或 Reflect.ownKeys
了),否则这三者足以应对绝大多数对象遍历的场景。
如何处理非可枚举属性或 Symbol 属性的遍历?
在JavaScript中,对象的属性不仅仅只有我们通常看到的那些字符串键。有些属性是“不可枚举”的,比如很多内置对象的方法(想想 Array.prototype.length
或者 Object.prototype.toString
),它们通常不希望被常规的遍历方式发现。另外,ES6引入的 Symbol
类型作为属性键,它们天生就是不可枚举的,并且不会被 for...in
或 Object.keys()
捕获。
那么,如果你真的需要访问这些“隐藏”的或者特殊类型的属性呢?JS也提供了相应的工具,只是它们不像 Object.keys()
那么常用,因为它们针对的是更特殊的场景。
Object.getOwnPropertyNames()
:遍历所有字符串键的属性(包括不可枚举的) 这个方法会返回一个由指定对象的所有自身属性(包括不可枚举属性,但不包括 Symbol 属性)的键组成的数组。当你需要检查一个对象的所有“常规”属性,无论它们是否可枚举时,它就派上用场了。const myObject = {}; Object.defineProperty(myObject, 'hiddenProp', { value: '我是不可枚举的', enumerable: false // 设置为不可枚举 }); myObject.visibleProp = '我是可枚举的'; console.log(Object.keys(myObject)); // ['visibleProp'] console.log(Object.getOwnPropertyNames(myObject)); // ['hiddenProp', 'visibleProp']
我个人觉得这个方法在调试或者需要深入分析对象结构时比较有用,比如你想看看一个第三方库的对象实例到底有哪些私有(但可访问)的属性。
Object.getOwnPropertySymbols()
:遍历所有 Symbol 属性 这个方法专门用来获取一个对象自身的所有 Symbol 属性的数组。由于 Symbol 属性设计之初就是为了提供一种独一无二的键,且默认不可枚举,所以你需要用这个方法才能“看到”它们。const SYMBOL_KEY = Symbol('myUniqueId'); const ANOTHER_SYMBOL = Symbol('another'); const dataWithSymbol = { name: 'Grace', age: 40, }; console.log(Object.keys(dataWithSymbol)); // ['name', 'age'] console.log(Object.getOwnPropertyNames(dataWithSymbol)); // ['name', 'age'] console.log(Object.getOwnPropertySymbols(dataWithSymbol)); // [Symbol(myUniqueId), Symbol(another)] // 你可以通过 Symbol 键来访问值 console.log(dataWithSymbol[SYMBOL_KEY]); // 这是个Symbol值
在一些需要为对象添加元数据,又不想这些元数据与普通字符串属性混淆或被常规遍历发现的场景,Symbol 属性和
Object.getOwnPropertySymbols()
就显得非常重要。Reflect.ownKeys()
:遍历所有自身属性(包括可枚举、不可枚举和 Symbol 属性) 这是Reflect
对象提供的一个方法,它返回一个由目标对象自身的属性键组成的数组。这个数组包含了所有字符串键(包括可枚举和不可枚举的)和所有 Symbol 键。可以说是最全面的一个。const REFLECT_SYMBOL = Symbol('reflectTest'); const myReflectObject = { a: 1, [REFLECT_SYMBOL]: 'symbol value' }; Object.defineProperty(myReflectObject, 'b', { value: 2, enumerable: false }); console.log(Reflect.ownKeys(myReflectObject)); // ['a', 'b', Symbol(reflectTest)]
Reflect.ownKeys()
在你需要获取一个对象所有“真实”的自身属性(无论其类型和可枚举性)时非常有用,比如在实现代理(Proxy)或者进行深度对象复制时,你可能就需要考虑这些。
总结一下,对于日常的业务开发,Object.keys()
、Object.values()
和 Object.entries()
几乎能满足你99%的需求。而 Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
和 Reflect.ownKeys()
则是更底层、更专业的工具,它们在你需要处理对象的“内部”属性或者 Symbol 属性时才会被用到。理解它们的存在,并在必要时能够正确使用,能让你对JavaScript对象的理解更深入一层。
今天关于《JS遍历对象的5种实用方法》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于的内容请关注golang学习网公众号!

- 上一篇
- Golang值类型与指针类型怎么选?实用场景解析

- 下一篇
- 七猫小说换绑手机教程详解
-
- 文章 · 前端 | 35秒前 |
- 滚动加载数据的JS实现技巧
- 468浏览 收藏
-
- 文章 · 前端 | 5分钟前 |
- HTML进度条是什么?如何自定义进度条代码
- 229浏览 收藏
-
- 文章 · 前端 | 15分钟前 |
- CSSborder属性详解与使用场景
- 196浏览 收藏
-
- 文章 · 前端 | 21分钟前 |
- HTML表单验证样式化技巧:CSS伪类应用
- 266浏览 收藏
-
- 文章 · 前端 | 23分钟前 |
- RAF不是事件循环部分,但与之紧密相关
- 420浏览 收藏
-
- 文章 · 前端 | 31分钟前 |
- CSS旋转进度条加载动画教程
- 166浏览 收藏
-
- 文章 · 前端 | 37分钟前 |
- 对象属性不可配置设置方法
- 198浏览 收藏
-
- 文章 · 前端 | 38分钟前 |
- 状态模式JS实现全解析
- 253浏览 收藏
-
- 文章 · 前端 | 46分钟前 |
- 判断JS变量是否为布尔值的技巧
- 482浏览 收藏
-
- 文章 · 前端 | 51分钟前 |
- JS文件是什么?怎么运行JS代码
- 179浏览 收藏
-
- 文章 · 前端 | 52分钟前 |
- HTML事件属性有哪些?7种onclick使用技巧
- 335浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 206次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 209次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 205次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 212次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 230次使用
-
- 优化用户界面体验的秘密武器:CSS开发项目经验大揭秘
- 2023-11-03 501浏览
-
- 使用微信小程序实现图片轮播特效
- 2023-11-21 501浏览
-
- 解析sessionStorage的存储能力与限制
- 2024-01-11 501浏览
-
- 探索冒泡活动对于团队合作的推动力
- 2024-01-13 501浏览
-
- UI设计中为何选择绝对定位的智慧之道
- 2024-02-03 501浏览