JS 对象遍历全解析

JS 对象遍历全解析:从基础到实战,新手也能轻松上手

在 JavaScript 开发中,对象(Object)是最常用的数据结构之一,几乎所有业务场景都会涉及到对象的遍历------比如获取对象的所有属性、筛选符合条件的键值对、批量处理对象数据等。

但很多新手在面对对象遍历时,总会困惑:for...inObject.keys() 有什么区别?什么时候用 Object.values()?如何遍历对象的原型链属性?

今天这篇文章,就带你彻底吃透 JS 对象遍历的所有常用方法,从基础用法到进阶技巧,再到实战场景对比,帮你避开坑点、灵活运用。

一、先明确:什么是 JS 对象?

在开始遍历之前,我们先简单回顾下 JS 对象的基础------对象是由 键(key)值(value) 组成的无序集合,键通常是字符串(ES6 后支持 Symbol),值可以是任意数据类型(字符串、数字、函数、对象等)。

示例对象(后文所有方法均基于此对象演示):

复制代码
// 基础对象
const user = {
  name: "掘金小册",
  age: 3,
  gender: "male",
  isVip: true,
  hobbies: ["coding", "writing"],
  // 方法
  sayHello: function() {
    console.log("Hello, Juejin!");
  }
};

// 原型链上的属性(用于后续演示区别)
Object.prototype.protoProp = "我是原型链上的属性";

二、常用对象遍历方法(按使用频率排序)

以下方法是开发中最常用的,重点掌握前 4 种,基本能覆盖 90%+ 的业务场景。

  1. for...in 循环(最基础,遍历可枚举属性)
    for...in 是最早的对象遍历方法,也是最基础的方式,它会遍历对象自身的可枚举属性原型链上的可枚举属性 (这是它的坑点,也是重点注意事项)。

基础用法

复制代码
// for...in 遍历对象
for (let key in user) {
  console.log("键:", key); // 输出对象的键
  console.log("值:", user[key]); // 输出对应的值
}

输出结果(重点看最后一行)

复制代码
键: name,值: 掘金小册
键: age,值: 3
键: gender,值: male
键: isVip,值: true
键: hobbies,值: [ 'coding', 'writing' ]
键: sayHello,值: [Function: sayHello]
键: protoProp,值: 我是原型链上的属性

核心注意事项(避坑关键)

  • 会遍历原型链上的可枚举属性(如上面的 protoProp),这通常不是我们想要的,所以一定要搭配 hasOwnProperty() 使用。
  • 不能遍历 Symbol 类型的键(后文会讲专门遍历 Symbol 的方法)。

正确用法(搭配 hasOwnProperty)

复制代码
for (let key in user) {
  // 只遍历对象自身的属性,过滤原型链属性
  if (user.hasOwnProperty(key)) {
    console.log("键:", key, "值:", user[key]);
  }
}

这样就不会输出原型链上的 protoProp 了,这是 for...in 的标准用法。

  1. Object.keys() + forEach(最常用,遍历自身可枚举键)

ES5 新增的 Object.keys() 方法,会返回一个包含对象自身可枚举属性键 的数组(不包含原型链属性,也不包含 Symbol 键),再搭配 forEach 循环,是目前开发中最常用的遍历方式。

基础用法

复制代码
 // 1. 获取对象自身的所有可枚举键,返回数组
const keys = Object.keys(user);
console.log(keys); // 输出:['name', 'age', 'gender', 'isVip', 'hobbies', 'sayHello']

// 2. 搭配 forEach 遍历
Object.keys(user).forEach(key => {
  console.log("键:", key);
  console.log("值:", user[key]);
});

核心优势

  • 自动过滤原型链属性,无需手动写 hasOwnProperty(),更简洁、更安全。
  • 返回的是数组,可直接使用数组的方法(如 forEachmapfilter 等),灵活度更高。

实战场景

筛选对象中值为布尔类型的键值对:

复制代码
const boolProps = {};
Object.keys(user).forEach(key => {
  if (typeof user[key] === "boolean") {
    boolProps[key] = user[key];
  }
});
console.log(boolProps); // 输出:{ isVip: true }
  1. Object.values() + forEach(遍历自身可枚举值)

ES2017(ES8)新增的 Object.values() 方法,和 Object.keys() 对应,它会返回一个包含对象自身可枚举属性值 的数组(不包含原型链属性、Symbol 键对应的值)。

基础用法

复制代码
 // 获取对象自身的所有可枚举值,返回数组
const values = Object.values(user);
console.log(values); 
// 输出:['掘金小册', 3, 'male', true, ['coding', 'writing'], [Function: sayHello]]

// 搭配 forEach 遍历值
Object.values(user).forEach(value => {
  console.log("值:", value);
});

实战场景

统计对象中所有数值类型的值的总和:

复制代码
const sum = Object.values(user).reduce((total, value) => {
  // 只累加数值类型的值
  return typeof value === "number" ? total + value : total;
}, 0);
console.log(sum); // 输出:3
  1. Object.entries() + forEach(遍历自身可枚举键值对)

同样是 ES2017 新增的方法,Object.entries() 是最灵活的遍历方法,它会返回一个包含对象自身可枚举键值对 的二维数组(每个子数组是 [key, value]),完美兼顾键和值的获取。

基础用法

复制代码
 // 获取对象自身的所有可枚举键值对,返回二维数组
const entries = Object.entries(user);
console.log(entries);
// 输出:[
//   ['name', '掘金小册'],
//   ['age', 3],
//   ...
// ]

// 搭配 forEach 遍历键值对
Object.entries(user).forEach(([key, value]) => {
  // 解构赋值,直接获取 key 和 value,更简洁
  console.log(`键:${key},值:${value}`);
});

实战场景

将对象转换为 Map(Map 支持更多灵活操作):

复制代码
// Object.entries() 可直接作为 Map 的构造参数
const userMap = new Map(Object.entries(user));
console.log(userMap.get("name")); // 输出:掘金小册

// 遍历 Map(补充)
userMap.forEach((value, key) => {
  console.log("键:", key, "值:", value);
});
  1. 进阶:遍历 Symbol 类型的键

如果对象的键是 Symbol 类型(ES6 新增,用于表示唯一键),上面的 4 种方法都无法遍历到,此时需要使用 Object.getOwnPropertySymbols() 方法。

示例用法

复制代码
// 定义一个包含 Symbol 键的对象
const obj = {
  [Symbol("id")]: 123,
  name: "测试"
};

// 遍历 Symbol 类型的键
Object.getOwnPropertySymbols(obj).forEach(symbolKey => {
  console.log("Symbol 键:", symbolKey);
  console.log("对应值:", obj[symbolKey]); // 输出:123
});
  1. 高阶:遍历所有属性(自身+原型链,可枚举+不可枚举)

开发中很少用到,但面试可能会问------如果需要遍历对象自身的所有属性 (包括不可枚举),或者原型链上的所有属性,可以使用以下方法:

  • Object.getOwnPropertyNames(obj):返回对象自身的所有属性键(包括不可枚举,不包括 Symbol)。
  • Reflect.ownKeys(obj):返回对象自身的所有属性键(包括不可枚举、Symbol 键),相当于 Object.keys(obj) + Object.getOwnPropertySymbols(obj) + 不可枚举键

示例

复制代码
// 遍历自身所有属性(包括不可枚举)
const allOwnKeys = Object.getOwnPropertyNames(user);
console.log(allOwnKeys); // 包含 sayHello(函数也是属性)

// 遍历自身所有属性(包括不可枚举、Symbol)
const allKeys = Reflect.ownKeys(user);
console.log(allKeys);

三、常用方法对比表(一目了然)

方法 遍历范围 是否包含原型链 是否包含 Symbol 键 是否包含不可枚举 适用场景
for...in 自身可枚举 + 原型链可枚举 是(需手动过滤) 兼容旧环境,需过滤原型链
Object.keys() 自身可枚举键 仅需获取键,搭配数组方法
Object.values() 自身可枚举值 仅需获取值,如统计、筛选
Object.entries() 自身可枚举键值对 需同时操作键和值(最常用)
Object.getOwnPropertySymbols() 自身 Symbol 键 遍历 Symbol 类型的键
Reflect.ownKeys() 自身所有键(可枚举+不可枚举+Symbol) 高阶场景,需获取所有自身属性

四、避坑指南(新手必看)

  1. 永远不要用 for...in 遍历数组!虽然数组也是对象,但 for...in 会遍历数组的原型链属性,还会按照"字符串索引"排序,导致遍历顺序错乱。
  2. 使用 for...in 必须搭配 hasOwnProperty(),否则会遍历到原型链上的无关属性,导致业务逻辑出错。
  3. Symbol 类型的键无法被 Object.keys()Object.values()for...in 遍历,需用专门的 Object.getOwnPropertySymbols()
  4. 对象是无序的!虽然 ES6 后对象的键会按照"数字优先、插入顺序"排列,但不要依赖对象的遍历顺序来实现业务逻辑(如需有序,建议用 Map)。

五、实战案例(综合运用)

需求:遍历一个用户对象,筛选出所有非函数类型的属性,将其转换为查询字符串(如:name=掘金小册&age=3)。

复制代码
const user = {
  name: "掘金小册",
  age: 3,
  gender: "male",
  isVip: true,
  sayHello: function() {
    console.log("Hello");
  }
};

// 1. 遍历对象,筛选非函数属性
const queryArr = Object.entries(user).filter(([key, value]) => {
  // 排除函数类型的属性
  return typeof value !== "function";
}).map(([key, value]) => {
  // 将键值对转换为 "key=value" 格式
  return `${key}=${encodeURIComponent(value)}`;
});

// 2. 拼接为查询字符串
const queryStr = queryArr.join("&");
console.log(queryStr);
// 输出:name=掘金小册&age=3&gender=male&isVip=true

解析:这里结合了 Object.entries()filter()map() 方法,既遍历了键值对,又完成了筛选和格式转换,是开发中非常典型的场景。

六、总结

JS 对象遍历的核心是"明确遍历范围"------你是要遍历自身属性,还是原型链属性?是要键、值,还是键值对?是包含 Symbol 或不可枚举属性?

对于绝大多数开发场景:

  • 仅需键 → 用 Object.keys()
  • 仅需值 → 用 Object.values()
  • 需键值对 → 用 Object.entries()(最常用)
  • 兼容旧环境 → 用 for...in + hasOwnProperty()

最后,祝大家在 JS 的世界里,遍历无坑,编码无忧!🚀

原文: https://juejin.cn/post/76028468

相关推荐
凡人叶枫2 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
Tony Bai2 小时前
再见,丑陋的 container/heap!Go 泛型堆 heap/v2 提案解析
开发语言·后端·golang
发现一只大呆瓜2 小时前
虚拟列表:支持“向上加载”的历史消息(Vue 3 & React 双版本)
前端·javascript·面试
小糯米6012 小时前
C++顺序表和vector
开发语言·c++·算法
css趣多多2 小时前
ctx 上下文对象控制新增 / 编辑表单显示隐藏的逻辑
前端
froginwe112 小时前
JavaScript 函数调用
开发语言
阔皮大师2 小时前
INote轻量文本编辑器
java·javascript·python·c#
lbb 小魔仙2 小时前
【HarmonyOS实战】React Native 表单实战:自定义 useReactHookForm 高性能验证
javascript·react native·react.js
独望漫天星辰2 小时前
C++ 多态深度解析:从语法规则到底层实现(附实战验证代码)
开发语言·c++