当前位置:首页 > 文章列表 > 文章 > 前端 > JS遍历对象的5种实用方法

JS遍历对象的5种实用方法

2025-08-19 15:45:34 0浏览 收藏

在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()。

JS如何遍历对象

在JavaScript里,遍历一个对象其实有那么几种核心方式,每种都有自己的脾气和适用场景。没有一招鲜吃遍天的,关键在于你手头的数据结构和想达成什么目的。简单来说,你需要访问对象的键、值,或者键值对,选择合适的工具能让你事半功倍,也能避免一些不必要的坑。

解决方案

要遍历JavaScript对象,我们通常会用到以下几种方法,它们各有侧重,也代表了JS发展中对对象遍历能力的不同演进:

  1. 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: 北京
  2. Object.keys() 结合数组方法(如 forEachfor...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]}`);
    }
  3. Object.values() 结合数组方法: 如果你只关心对象的值,Object.values() 就很方便了。它返回一个由给定对象自身的所有可枚举属性的值组成的数组。

    const myObject = {
        a: 1,
        b: 'hello',
        c: true
    };
    
    Object.values(myObject).forEach(value => {
        console.log(value);
    });
    // 输出:
    // 1
    // hello
    // true
  4. 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 中算是历史比较悠久的遍历方式了,它设计之初是为了枚举对象的所有可枚举属性,包括那些从原型链上继承来的属性。这听起来好像很强大,但实际上,这正是它最容易让人“翻车”的地方。

你可能会在一些老旧的代码库里看到它的身影,或者在需要遍历非数组对象(比如纯粹的键值对集合)时偶尔被用到。它的一个优点是,它能直接访问到属性名,并且在某些特定场景下,比如你需要检查一个对象的所有属性,包括继承的,它可能显得直接。

然而,它的陷阱也显而易见:

  1. 遍历原型链: 这是最常见的坑。如果你不加 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)) 这层防护。这就像是在一个不那么安全的街区走路,你总得给自己加个保险。

  2. 属性顺序不确定性(旧版本JS): 在ES5及之前的版本,for...in 遍历属性的顺序是无法保证的,它取决于JavaScript引擎的实现。虽然现代JS引擎(ES2015+)通常会按照属性创建的顺序来遍历数字键和字符串键,但对于混合了数字和非数字键的复杂对象,或者你对顺序有严格要求时,它依然不是最可靠的选择。

  3. 性能考量: 相比于 Object.keys() 配合数组方法,for...in 在某些场景下可能会略慢,因为它需要检查原型链。当然,对于小对象来说,这点差异几乎可以忽略不计。

所以,我的建议是:如果你不是在维护一些老代码,或者确实需要遍历原型链上的可枚举属性(这种场景其实很少),那么尽量避免直接使用 for...in。现代的 Object.keys()Object.values()Object.entries() 配合 for...offorEach 循环,通常是更安全、更可预测、也更符合现代编程习惯的选择。它们直接返回数组,你可以用数组的所有强大功能来处理,心智负担也小很多。

使用 Object.keys()Object.values()Object.entries() 遍历对象的最佳实践是什么?

当你谈到现代JavaScript中遍历对象,Object.keys()Object.values()Object.entries() 几乎是绕不开的。它们是ES6(ES2015)引入的,提供了一种更安全、更灵活、也更“函数式”的遍历方式。我个人在写业务代码时,如果不是有特殊原因,基本都倾向于使用它们,心里踏实。

它们的“最佳实践”其实很简单,就是充分利用它们返回数组的特性,然后结合数组的各种遍历方法。

  1. 返回数组的优势: 这三者最大的优势在于它们都返回一个新数组。这意味着你不再需要担心原型链上的属性污染,也不用手动去写 hasOwnProperty 这样的检查。它们只关注对象自身的、可枚举的属性。一旦你拿到了数组,那么JavaScript数组的丰富API就为你敞开了大门。

    • Object.keys(obj): 获取所有键的数组。
    • Object.values(obj): 获取所有值的数组。
    • Object.entries(obj): 获取所有 [key, value] 对的数组。
  2. 配合 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}`);
    });
  3. 配合 for...of 进行结构化遍历:for...of 循环是遍历可迭代对象(包括数组)的利器。它比 forEach 更灵活,支持 breakcontinue,并且在异步操作时也更方便。特别是配合 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 中中断循环
        }
    }
  4. 配合 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.getOwnPropertyNamesReflect.ownKeys 了),否则这三者足以应对绝大多数对象遍历的场景。

如何处理非可枚举属性或 Symbol 属性的遍历?

在JavaScript中,对象的属性不仅仅只有我们通常看到的那些字符串键。有些属性是“不可枚举”的,比如很多内置对象的方法(想想 Array.prototype.length 或者 Object.prototype.toString),它们通常不希望被常规的遍历方式发现。另外,ES6引入的 Symbol 类型作为属性键,它们天生就是不可枚举的,并且不会被 for...inObject.keys() 捕获。

那么,如果你真的需要访问这些“隐藏”的或者特殊类型的属性呢?JS也提供了相应的工具,只是它们不像 Object.keys() 那么常用,因为它们针对的是更特殊的场景。

  1. 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']

    我个人觉得这个方法在调试或者需要深入分析对象结构时比较有用,比如你想看看一个第三方库的对象实例到底有哪些私有(但可访问)的属性。

  2. 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() 就显得非常重要。

  3. 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值类型与指针类型怎么选?实用场景解析Golang值类型与指针类型怎么选?实用场景解析
上一篇
Golang值类型与指针类型怎么选?实用场景解析
七猫小说换绑手机教程详解
下一篇
七猫小说换绑手机教程详解
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    511次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    498次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 千音漫语:智能声音创作助手,AI配音、音视频翻译一站搞定!
    千音漫语
    千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
    206次使用
  • MiniWork:智能高效AI工具平台,一站式工作学习效率解决方案
    MiniWork
    MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
    209次使用
  • NoCode (nocode.cn):零代码构建应用、网站、管理系统,降低开发门槛
    NoCode
    NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
    205次使用
  • 达医智影:阿里巴巴达摩院医疗AI影像早筛平台,CT一扫多筛癌症急慢病
    达医智影
    达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
    212次使用
  • 智慧芽Eureka:更懂技术创新的AI Agent平台,助力研发效率飞跃
    智慧芽Eureka
    智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
    230次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码