当前位置: 首页 > backend >正文

记一次错误 深拷贝 key值全部小写

深度克隆中键名转换的两种方式差异分析

问题背景

在实现一个深度克隆并将所有键名转换为小写的函数时,发现两行看似相似的代码会产生完全不同的结果:

// 方式1:使用原始值(错误方式)
newObj[key.toLowerCase()] = obj[key];// 方式2:使用递归处理后的值(正确方式)
newObj[key.toLowerCase()] = newObj[key];

核心差异分析

方式1的问题:使用原始值 obj[key]

function deepClone(obj) {if (typeof obj !== "object" || obj === null) {return obj;}const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepClone(obj[key]);        // 递归处理,存储到原键名if (key.toLowerCase() !== key) {newObj[key.toLowerCase()] = obj[key];   // ❌ 使用原始值覆盖delete newObj[key];}}}return newObj;
}

执行过程分析:

当处理对象 {A: "1", D: {E: "3", F: "22"}} 时:

  1. 处理键 A

    • newObj['A'] = deepClone("1")newObj['A'] = "1"
    • key.toLowerCase() !== key 为 true
    • newObj['a'] = obj['A']newObj['a'] = "1"(原始值)
    • delete newObj['A']
  2. 处理键 D

    • newObj['D'] = deepClone({E: "3", F: "22"})
    • 递归调用返回 {E: "3", F: "22"}(内层键名未转换!)
    • newObj['d'] = obj['D']newObj['d'] = {E: "3", F: "22"}(原始对象)
    • delete newObj['D']

问题根源:使用 obj[key] 会直接获取原始对象,绕过了递归处理的结果。

方式2的正确实现:使用递归结果 newObj[key]

function deepClone(obj) {if (typeof obj !== "object" || obj === null) {return obj;}const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepClone(obj[key]);        // 递归处理,存储到原键名if (key.toLowerCase() !== key) {newObj[key.toLowerCase()] = newObj[key]; // ✅ 使用递归处理后的值delete newObj[key];}}}return newObj;
}

执行过程分析:

当处理同样的对象时:

  1. 处理键 A

    • newObj['A'] = deepClone("1")newObj['A'] = "1"
    • newObj['a'] = newObj['A']newObj['a'] = "1"(递归结果)
    • delete newObj['A']
  2. 处理键 D

    • newObj['D'] = deepClone({E: "3", F: "22"})
    • 递归调用返回 {e: "3", f: "22"}(内层键名已转换!)
    • newObj['d'] = newObj['D']newObj['d'] = {e: "3", f: "22"}(递归结果)
    • delete newObj['D']

代码示例对比

测试代码

const testObj = {A: "1",B: {C: "2",D: {E: "3",F: "22"}}
};// 错误方式的结果
const wrongResult = deepCloneWrong(testObj);
console.log("错误方式:", JSON.stringify(wrongResult, null, 2));
// 输出:
// {
//   "a": "1",
//   "b": {
//     "C": "2",        // ❌ 保持大写
//     "D": {           // ❌ 保持大写
//       "E": "3",      // ❌ 保持大写  
//       "F": "22"      // ❌ 保持大写
//     }
//   }
// }// 正确方式的结果
const correctResult = deepCloneCorrect(testObj);
console.log("正确方式:", JSON.stringify(correctResult, null, 2));
// 输出:
// {
//   "a": "1",
//   "b": {
//     "c": "2",        // ✅ 转换为小写
//     "d": {           // ✅ 转换为小写
//       "e": "3",      // ✅ 转换为小写
//       "f": "22"      // ✅ 转换为小写
//     }
//   }
// }

性能影响分析

操作步骤对比

方式1(错误但低效)

  1. 递归处理 → 创建新对象
  2. 存储到原键名
  3. 复制原始值到小写键名
  4. 删除原键名

方式2(正确且高效)

  1. 递归处理 → 创建新对象
  2. 存储到原键名
  3. 移动递归结果到小写键名
  4. 删除原键名

最优方案(推荐)

function deepCloneOptimal(obj) {if (typeof obj !== "object" || obj === null) {return obj;}const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {// 直接使用小写键名,避免额外操作const clonedValue = deepCloneOptimal(obj[key]);const newKey = key.toLowerCase();newObj[newKey] = clonedValue;}}return newObj;
}

性能优势

  • 减少属性操作次数(从3次减少到1次)
  • 避免 delete 操作(相对昂贵)
  • 内存使用更高效

关键知识点总结

1. 递归与引用的关系

  • obj[key]:指向原始对象的引用,未经过递归处理
  • newObj[key]:指向递归处理后的新对象

2. 深度优先处理原则

在深度克隆中,必须确保:

  1. 先处理子对象(递归调用)
  2. 再处理当前层级的转换
  3. 使用处理后的结果,而非原始引用

3. JavaScript 对象属性操作成本

操作类型按成本从低到高:

  1. 直接赋值obj.key = value(最快)
  2. 属性复制obj.newKey = obj.oldKey
  3. 属性删除delete obj.key(最慢,可能触发对象重构)

断点调试
在这里插入图片描述
条件断点 27行 key==‘D’
打不上26行
在这里插入图片描述

http://www.xdnf.cn/news/13945.html

相关文章:

  • 三次握手建立连接,四次挥手释放连接——TCP协议的核心机制
  • 上海市计算机学会竞赛平台2022年6月月赛丙组模糊匹配
  • 蚂蚁国际计划在香港和新加坡推出稳定币
  • 关于UEFI:UEFI/BIOS 固件分析
  • 【51单片机】6. 定时器、按键切换流水灯时钟Demo
  • MFC对话框程序使用线程方式更新窗体文本框内容(vs2019)
  • 多平台联动营销:品融电商助食品品牌打造电商“多栖”增长引擎
  • GetX例子:在一个组件里更新状态,在另一个组件里获取更新的数据
  • [Linux] -- 大文件拆分、合并与校验全解析:处理 GB/TB 级文件
  • 2024 一带一路暨金砖国家职业技能大赛(金砖国家未来技能和技术挑战赛)
  • openEuler虚拟机中容器化部署
  • c++虚表的调用
  • CSS 基础选择器、文字控制属性
  • 蓝牙协议GAP(Generic Access Profile)
  • 报表工具顶尖对决系列 — Echarts 展现与导出
  • CC工具箱使用指南:【断线连接】
  • Oracle OCP认证考试考点详解083系列18
  • 如何在SOLIDWORKS工程图中添加材料明细表?
  • 关于联咏(Novatek )自动曝光中Lv值的计算方式实现猜想
  • win11系统安装jdk11教程
  • day22
  • Python 实现一个带进度条的 URL 批量下载工具(含 GUI 界面)
  • 第三节 类型系统进阶-接口(interface)与类型别名(type)的区别
  • 算法导论第三章:数据结构艺术与高效实现
  • 【Mysql及各种关系型数据库全面对比与深度解析(2025版)】
  • 第六章网络互联设备
  • 前端八股文 - CSS 篇
  • GPU-CPU-FPGA三维异构计算统一内存架构实践:基于OpenCL的跨设备Kernel动态迁移方案(附内存一致性协议设计)
  • 深入剖析 Spring @Bean 注解:灵活定义与掌控你的 Bean
  • 权限管理设计思路