上海高端网站建设公司用手机建立自己的网站
2026/4/6 2:31:28 网站建设 项目流程
上海高端网站建设公司,用手机建立自己的网站,一站式+wordpress,创世通网站建设在JavaScript日常开发中#xff0c;数据拷贝是绕不开的操作#xff0c;但很多人都踩过“改新对象、原对象跟着变”的坑#xff0c;排查半天发现根源是没分清浅拷贝和深拷贝。这篇文章从底层存储机制讲起#xff0c;结合实际业务场景和可直接复用的代码案例#xff0c;把深…在JavaScript日常开发中数据拷贝是绕不开的操作但很多人都踩过“改新对象、原对象跟着变”的坑排查半天发现根源是没分清浅拷贝和深拷贝。这篇文章从底层存储机制讲起结合实际业务场景和可直接复用的代码案例把深浅拷贝的核心逻辑、实现方式和边界情况讲透帮大家在开发中精准选型、避免踩雷。一、前置核心JS数据类型的存储逻辑想分清深浅拷贝必须先吃透JS数据类型的存储规则——这是所有拷贝行为的底层逻辑没搞懂这个后面的内容都是空中楼阁。1. 数据类型分类及存储差异JS数据分两大类存储位置和拷贝特性完全不同直接决定了拷贝后的表现类型分类包含具体类型存储位置核心拷贝特点基本数据类型Number、String、Boolean、Null、Undefined、Symbol、BigInt栈内存Stack拷贝时直接复制「值」新老数据相互独立修改互不影响引用数据类型Object数组、普通对象、函数、Date、RegExp等栈存引用地址堆存实际值Heap默认拷贝「引用地址」新老对象共享同一块堆内存数据2. 直观对比基本类型与引用类型拷贝差异基本类型拷贝本质是值拷贝天然具备“深拷贝”的效果修改新变量不会牵连原变量。// 基本类型拷贝示例 let num1 100; let num2 num1; // 直接复制num1的值 num2 200; // 修改新变量 console.log(num1); // 100原数据不受影响 console.log(num2); // 200新数据独立引用类型默认拷贝只复制引用地址新老对象指向同一块堆内存改一个就会影响另一个这就是浅拷贝的核心问题。// 引用类型默认浅拷贝 let obj1 { name: 张三, age: 22 }; let obj2 obj1; // 复制的是引用地址而非实际数据 obj2.name 李四; // 修改新对象属性 console.log(obj1.name); // 李四原对象被同步修改 console.log(obj2.name); // 李四关键结论浅拷贝和深拷贝的差异仅存在于引用数据类型中。基本类型的拷贝都是“值拷贝”不存在深浅之分。二、浅拷贝只做一层的“表面功夫”1. 浅拷贝的核心特性浅拷贝针对引用数据类型仅复制对象表层结构——也就是栈内存引用地址对应的第一层数据。如果对象存在多层嵌套比如对象里套对象、数组里套对象内层的引用类型依然共享堆内存修改内层数据会同步影响原对象。2. 常用浅拷贝实现方式附实战案例1数组浅拷贝数组的slice()、concat()方法以及ES6扩展运算符都是常用的浅拷贝方式用法简单但要注意内层数据共享的问题。const arr1 [1, 2, { score: 90 }]; // 三种常用浅拷贝方式 const arr2 [...arr1]; // 扩展运算符最简洁 const arr3 arr1.slice(); // slice方法不传参复制全量 const arr4 arr1.concat(); // concat方法空参数拼接 // 修改内层嵌套对象 arr2[2].score 100; // 所有浅拷贝数组的内层数据都会同步变化 console.log(arr1[2].score); // 100原数组受影响 console.log(arr3[2].score); // 100 console.log(arr4[2].score); // 1002对象浅拷贝Object.assign()和ES6扩展运算符是对象浅拷贝的主流方式适合单层对象的复用场景。const obj1 { a: 1, b: { c: 2 } }; // 两种对象浅拷贝方式 const obj2 Object.assign({}, obj1); // Object.assign const obj3 { ...obj1 }; // 扩展运算符更直观 // 修改内层嵌套对象 obj2.b.c 3; // 原对象和其他浅拷贝对象的内层数据同步变化 console.log(obj1.b.c); // 3原对象受影响 console.log(obj3.b.c); // 33. 浅拷贝的适用场景浅拷贝性能开销小、实现简单不用做递归处理适合以下场景对象/数组为单层结构无任何嵌套的引用类型仅复用表层数据且内层数据为只读状态不涉及修改操作。三、深拷贝完全独立的“全量复制”1. 深拷贝的核心特性深拷贝会递归遍历引用数据类型的所有层级把每一层的实际数据都复制到新的堆内存中最终生成一个和原对象完全独立的新对象。无论修改新对象的哪一层数据都不会对原对象产生任何影响。2. 三种深拷贝实现方式从简单到生产可用1简易版JSON.parse(JSON.stringify())这是日常开发中最常用的快速深拷贝方案不用依赖任何库一行代码就能实现适合简单场景。const obj1 { name: 张三, info: { age: 22, hobby: [篮球, 游戏] } }; // 一行代码实现深拷贝 const obj2 JSON.parse(JSON.stringify(obj1)); // 修改新对象的内层数据 obj2.info.age 25; obj2.info.hobby[0] 足球; // 原对象数据不受影响 console.log(obj1.info.age); // 22 console.log(obj1.info.hobby[0]); // 篮球注意局限性这种方式有明显短板生产环境需谨慎使用不支持以下场景无法拷贝函数、Undefined、Symbol会被直接忽略无法处理循环引用对象会直接报错导致程序中断特殊对象拷贝失真Date转为字符串、RegExp转为空对象。我们用一个案例直观感受这些问题const obj { fn: () console.log(test), // 函数类型 time: new Date(), // Date类型 undef: undefined, // Undefined类型 sym: Symbol(foo) // Symbol类型 }; const copyObj JSON.parse(JSON.stringify(obj)); console.log(copyObj.fn); // undefined函数被忽略 console.log(copyObj.time); // 字符串Date失真 console.log(copyObj.undef); // 不存在Undefined被忽略 console.log(copyObj.sym); // 不存在Symbol被忽略2进阶版手动实现递归深拷贝兼容特殊场景手动实现深拷贝能帮我们吃透底层逻辑还能自定义处理特殊类型。下面是优化后的完整实现兼容Date、RegExp和循环引用可直接用于中小型项目。/** * 优化版递归深拷贝兼容Date、RegExp、循环引用 * param {*} target 要拷贝的目标数据 * param {WeakMap} map 缓存已拷贝对象解决循环引用 * returns 拷贝后的新数据 */ function deepClone(target, map new WeakMap()) { // 1. 处理基本类型和null直接返回值无需拷贝 if (typeof target ! object || target null) { return target; } // 2. 处理循环引用已拷贝过则直接返回缓存对象避免死循环 if (map.has(target)) { return map.get(target); } // 3. 处理Date类型创建新Date对象保留原时间 if (target instanceof Date) { const newDate new Date(target.getTime()); map.set(target, newDate); return newDate; } // 4. 处理RegExp类型保留原正则表达式的源和修饰符 if (target instanceof RegExp) { const newReg new RegExp(target.source, target.flags); map.set(target, newReg); return newReg; } // 5. 处理数组创建新数组递归拷贝每一项 if (target instanceof Array) { const newArr []; map.set(target, newArr); target.forEach(item newArr.push(deepClone(item, map))); return newArr; } // 6. 处理普通对象创建新对象遍历自身属性递归拷贝 if (target instanceof Object) { const newObj {}; map.set(target, newObj); // 只拷贝自身可枚举属性避免拷贝原型链上的属性 for (const key in target) { if (target.hasOwnProperty(key)) { newObj[key] deepClone(target[key], map); } } return newObj; } } // 测试包含循环引用和特殊类型 const obj1 { a: 1, date: new Date(), reg: /test/g, arr: [1, { b: 2 }] }; // 制造循环引用obj1指向自身 obj1.self obj1; const obj2 deepClone(obj1); // 验证拷贝效果 console.log(obj2.date instanceof Date); // trueDate类型正常 console.log(obj2.reg instanceof RegExp); // trueRegExp类型正常 console.log(obj2.self obj2); // true循环引用处理正常 // 修改新对象内层数据验证独立性 obj2.arr[1].b 100; console.log(obj1.arr[1].b); // 2原数据无变化这个实现的核心优化点用WeakMap缓存已拷贝对象解决循环引用导致的递归死循环针对性处理Date和RegExp类型避免拷贝失真基本能覆盖大部分业务场景。3生产版第三方库lodash.cloneDeep()生产环境中不建议自己造轮子——成熟的第三方库已经处理了所有边界情况包括函数、循环引用、特殊对象等稳定性和兼容性更有保障。最常用的就是lodash的cloneDeep()方法。// 1. 安装lodashnpm/yarn // npm install lodash --save // yarn add lodash // 2. 引入并使用可按需引入减少体积 const _ require(lodash); // 按需引入方式const cloneDeep require(lodash/cloneDeep); const obj1 { name: 李四, fn: () console.log(hello deep clone), // 函数类型 date: new Date(), // Date类型 reg: /test/g, // RegExp类型 info: { age: 24 } }; const obj2 _.cloneDeep(obj1); // 修改新对象内层数据 obj2.info.age 26; // 验证效果原对象无变化特殊类型拷贝正常 console.log(obj1.info.age); // 24 console.log(obj2.fn()); // hello deep clone函数正常 console.log(obj2.date instanceof Date); // trueDate正常四、实战选型指南深浅拷贝怎么选开发中不用盲目追求深拷贝结合业务场景选型才是最优解既保证功能又兼顾性能。整理了常见场景的选型建议业务场景推荐拷贝方式选择理由单层对象/数组无嵌套引用类型浅拷贝扩展运算符/Object.assign性能最优实现简单无多余递归开销多层嵌套引用类型需完全独立数据深拷贝避免修改新数据影响原对象保证数据安全性数据含函数、循环引用、特殊对象lodash.cloneDeep()JSON方式无法处理手动实现成本高库更稳定简单演示、临时数据处理无特殊类型JSON.parse(JSON.stringify())一行代码搞定无需依赖第三方库高效便捷五、总结与实战技巧其实深浅拷贝的核心很简单浅拷贝抄“地址”深拷贝抄“全量数据”。记住以下几点就能在开发中灵活应对基本类型拷贝都是值拷贝深浅拷贝仅针对引用类型浅拷贝适合单层结构深拷贝适合多层嵌套结构避免过度使用深拷贝性能开销更大生产环境优先用lodash.cloneDeep()简易场景用JSON方法学习/面试场景可手动实现递归拷贝快速验证是否为深拷贝用“”判断新老对象若结果为false且内层属性修改互不影响即为深拷贝。深浅拷贝是JS基础中的重点也是面试高频考点吃透底层原理和选型逻辑不仅能避免开发踩坑还能应对面试中的延伸问题。文中所有代码都已实测可用大家可以直接复制到项目中复用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询