深入解析JavaScript中对象的多种遍历方法及JSON字符串与对象之间的转换技巧,包含完整代码示例、常见陷阱分析与结构图解。
JavaScript对象遍历方法与JSON数据转换代码案例
时间:2026-10-30
浏览:229
文章目录
在前端业务与服务端接口交互过程中,JavaScript 对象的属性遍历以及与 JSON 字符串互相转换是最基础也是最高频的操作。本文系统梳理各种遍历手段的差异、JSON 双向转换的具体规则,并通过完整的代码案例与图解帮助开发者正确选型与避坑。
一、JavaScript 对象遍历的核心机制与方法对比
JavaScript 提供了多种遍历普通对象属性的方式。开发者常因未能区分“自身属性与原型链属性”以及“枚举属性与不可枚举属性”而引发偶发 Bug。理解各种遍历手段的边界是写出健壮代码的前提。
1. for...in 循环及原型链属性防护
for...in 语句以任意顺序遍历一个对象自有的及原型链上继承的可枚举属性(Symbol 属性除外)。正因为其会沿着原型链向上查找,容易意外遍历出原型中添加的自定义函数或属性。
// 示例对象与原型继承定义
const protoObj = { inheritedRole: "guest" };
const userProfile = Object.create(protoObj);
userProfile.name = "Alice";
userProfile.age = 26;
// 使用 for...in 遍历并过滤自身属性
for (const key in userProfile) {
// 推荐使用 ES2022 引入的 Object.hasOwn 进行属性归属判断
if (Object.hasOwn(userProfile, key)) {
console.log(`[自有属性] ${key}: ${userProfile[key]}`);
} else {
console.log(`[原型属性] ${key}: ${userProfile[key]}`);
}
}
// 示例输出:
// [自有属性] name: Alice
// [自有属性] age: 26
// [原型属性] inheritedRole: guest
2. Object.keys()、Object.values() 与 Object.entries()
在现代 ECMAScript 开发中,推荐优先使用 Object.keys()、Object.values() 和 Object.entries()。这组方法只返回对象自身(非继承)的可枚举字符串属性数组,能够无缝结合 forEach()、map() 或 for...of 进行遍历。
const product = {
id: "P-1001",
name: "机械键盘",
price: 299,
stock: 45
};
// 1. 获取所有键
const keys = Object.keys(product);
console.log("所有键:", keys); // 示例输出: ['id', 'name', 'price', 'stock']
// 2. 获取所有值
const values = Object.values(product);
console.log("所有值:", values); // 示例输出: ['P-1001', '机械键盘', 299, 45]
// 3. 遍历键值对 (结合解构赋值)
for (const [key, val] of Object.entries(product)) {
console.log(`字段名: ${key.padEnd(6, ' ')} -> 当前值: ${val}`);
}
// 示例输出:
// 字段名: id -> 当前值: P-1001
// 字段名: name -> 当前值: 机械键盘
// 字段名: price -> 当前值: 299
// 字段名: stock -> 当前值: 45
3. 处理 Symbol 属性与不可枚举属性
如果对象的键中使用了 Symbol 或者通过 Object.defineProperty 定义了 enumerable: false,前述方法均会静默忽略。此时需使用 Object.getOwnPropertySymbols() 或全量反射接口 Reflect.ownKeys()。
const privateKey = Symbol("token");
const account = {
username: "developer_01",
[privateKey]: "secret_hash_9876"
};
// 定义一个不可枚举属性
Object.defineProperty(account, "internalId", {
value: "SYS-999",
enumerable: false
});
// Reflect.ownKeys 可一次性读取所有自身键 (包括字符串、Symbol、不可枚举)
const allKeys = Reflect.ownKeys(account);
console.log("全量键:", allKeys);
// 示例输出: ['username', 'internalId', Symbol(token)]
二、JSON 数据转换标准流程与底层机制
JSON(JavaScript Object Notation)是一种纯文本数据交换格式。JavaScript 内置的全局 JSON 对象提供了 JSON.stringify()(对象序列化为 JSON 文本)和 JSON.parse()(JSON 文本解析为对象)两个核心 API。二者不仅支持直接转换,还提供了用于深度定制处理的 replacer 和 reviver 回调参数。
1. JSON.stringify() 语法与高级用法
JSON.stringify(value, replacer, space) 支持接收三个参数:
value: 将要转为 JSON 字符串的值。replacer(可选): 转换函数或键名白名单数组。若是函数,对象的每个属性都会调用它,返回的值决定最终输出。space(可选): 用于美化输出的空格数或占位字符(最多 10 个空格)。
const orderData = {
orderId: "ORD-20261024",
customer: "Zhang San",
status: "PAID",
paymentTime: new Date("2026-10-24T14:00:00Z"),
internalAuthToken: "hidden_token_888", // 敏感字段需过滤
customTags: ["VIP", "Rush"]
};
// 案例 1:使用白名单数组过滤指定属性
const briefJson = JSON.stringify(orderData, ["orderId", "status", "customer"]);
console.log("白名单过滤结果:", briefJson);
// 示例输出: {"orderId":"ORD-20261024","status":"PAID","customer":"Zhang San"}
// 案例 2:使用 replacer 函数处理特定字段并格式化缩进
const formattedJson = JSON.stringify(orderData, (key, value) => {
if (key === "internalAuthToken") {
return undefined; // 返回 undefined 会使该属性在序列化中被剔除
}
return value;
}, 2);
console.log("格式化输出:\n" + formattedJson);
2. JSON.parse() 与日期字段自动还原
JSON 规范中并没有“日期”类型,Date 对象在经过 JSON.stringify 处理后会被转换为 ISO 格式的字符串。当使用 JSON.parse 读回时,默认仍是普通字符串。通过传入 reviver 函数,可以在解析阶段将特定格式的字符串自动重建为真实 Date 实例。
const rawJsonString = '{"reportTitle":"月度总结","publishedAt":"2026-10-24T06:00:00.000Z","readCount":1240}';
// 定义 ISO 8601 时间戳正则
const isoDateFormat = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
const parsedReport = JSON.parse(rawJsonString, (key, value) => {
if (typeof value === "string" && isoDateFormat.test(value)) {
return new Date(value); // 还原为真实 Date 对象
}
return value;
});
console.log("还原后字段类型:", Object.prototype.toString.call(parsedReport.publishedAt));
// 示例输出: [object Date]
console.log("可直接调用日期方法:", parsedReport.publishedAt.getFullYear());
// 示例输出: 2026
三、实战案例:复杂 JSON 对象的遍历清洗与数据重构
在实际业务中,接口返回的数据往往包含多层嵌套、空字符串、null 占位或者冗余字段。我们需要将对象遍历技术与 JSON 转换结合起来,完成数据过滤、深度转换和落盘准备。
下面是一个可直接执行的完整案例:编写一个 cleanAndSerialize 函数,它能深层遍历任意复杂的嵌套对象,递归剔除所有值为 null、undefined 或空字符串的属性,并将驼峰命名(camelCase)字段统一转换为下划线(snake_case)命名输出为 JSON。
// 驼峰转下划线辅助函数
function camelToSnake(str) {
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
}
// 递归遍历并清洗对象
function cleanAndTransformObject(target) {
// 基本类型及 null 直接返回
if (target === null || typeof target !== "object") {
return target;
}
// 数组处理:保持索引顺序,递归清洗子项
if (Array.isArray(target)) {
return target
.map(item => cleanAndTransformObject(item))
.filter(item => item !== null && item !== undefined && item !== "");
}
// 普通对象:使用 Object.entries() 遍历键值对并重构
const cleanedResult = {};
for (const [key, value] of Object.entries(target)) {
// 过滤无效空值
if (value === null || value === undefined || value === "") {
continue;
}
const newKey = camelToSnake(key);
cleanedResult[newKey] = cleanAndTransformObject(value);
}
return cleanedResult;
}
// 测试数据
const rawApiResponse = {
userName: "Bob",
userAge: 30,
emptyField: "",
nullableScore: null,
contactDetails: {
phoneNumber: "13800000000",
backupEmail: null,
homeAddress: "北京市海淀区"
},
skillTags: ["JavaScript", "", "Node.js"]
};
// 执行清洗与转换
const cleanedData = cleanAndTransformObject(rawApiResponse);
const finalJsonString = JSON.stringify(cleanedData, null, 2);
console.log("最终清洗转换后的 JSON 字符串:\n" + finalJsonString);
// 示例输出:
// {
// "user_name": "Bob",
// "user_age": 30,
// "contact_details": {
// "phone_number": "13800000000",
// "home_address": "北京市海淀区"
// },
// "skill_tags": [
// "JavaScript",
// "Node.js"
// ]
// }
四、常见陷阱与技术选型总结
在日常处理对象遍历与 JSON 转换时,有几个关键边界需要重点防范:
- 循环引用崩溃:当一个对象的某个属性直接或间接指向自身时,执行
JSON.stringify会抛出TypeError: Converting circular structure to JSON。解决办法是使用WeakSet记录已访问对象,或者在序列化前解除环形引用。 - 特殊数据类型的丢失:
undefined、函数以及Symbol作为对象属性值时,在JSON.stringify中会被直接忽略;若出现在数组中,则会被转为null。NaN和Infinity会被转为null。RegExp、Map、Set、Error会被转为空对象{}。
- 深拷贝的误用:开发者常使用
JSON.parse(JSON.stringify(obj))实现快速深拷贝,但该方式仅适用于包含纯字面量类型、布尔值、数字、字符串和普通数组的对象。若包含日期、函数或正则表达式,会导致原型链和类型丢失。现代浏览器推荐优先使用原生structuredClone(obj)。
Object.keys() 与 Object.entries(),可规避原型链继承干扰;在进行 JSON 序列化和解析时,合理利用 replacer 和 reviver 函数能有效完成敏感信息脱敏和特殊对象类型(如 Date)的无损还原。面对深拷贝需求,应优先采用标准 API structuredClone(),避免简易 JSON 序列化带来的类型丢失。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。