一、深拷贝和浅拷贝
基本数据类型: 字符串(string)、数值(number)、布尔值(boolean)、undefined、null ;
引用数据类型: 对象(Object)、数组(Array)、函数(Function);
深拷贝和浅拷贝只针对Object和Array引用数据类型
浅拷贝: 只复制了原始对象的引用,也就是栈中指向堆中同一对象的地址。这意味着,新的对象并不是在堆中重新分配了内存空间,而是与原始对象共享同一块空间。因此,如果修改了其中一个对象,另一个对象也会受到影响。(见下方图解)
深拷贝: 则会将原始对象及其所有子对象都复制到堆中,并返回一个指向新对象的引用。这样,新的对象与原始对象之间不存在任何关联,修改其中一个对象不会影响另一个对象。(见下方图解)
二、实现深拷贝的方法
var foo ={name:'绝绝子',info:{age:18}}
1、JSON.parse()和JSON.stringify()
可以将JavaScript对象序列化为一个JSON字符串,再使用JSON.parse()将其解析为一个新的对象,从而实现深拷贝。需要注意的是,该方法只能处理部分数据类型,例如字符串、数字、布尔值、数组和对象等。对于函数、Date对象、正则表达式等类型,则无法正确地进行序列化和反序列化。
当对象内存在undefined、symbol、function类型的属性时,在序列化过程中会被忽略;当属性为NaN 和 Infinity 格式的数值及 null 都会被当做 null。
const newFoo =JSON.parse(JSON.stringify(foo));
console.log(foo, newFoo);// { name: '绝绝子', info: { age: 18 } } { name: '绝绝子', info: { age: 18 } }
foo.info.age =25
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 25 } } { name: '绝绝子', info: { age: 18 } }
2、手写递归函数
可以手写一个递归函数来遍历对象及其子属性,并创建一个全新的对象来实现深拷贝。这种方法非常灵活,对于任何类型的可变对象都适用。但需要注意循环引用的问题,即对象中存在相互引用的情况。
constdeepCopy=(obj)=>{// 判断传入的值是否为一个对象if(obj ===null&&typeof obj !=="object"){return obj;}// 判断对象的类型 注意这里不考虑包装类对象if(Object.prototype.toString.call(obj)==="[object Date]"){returnnewDate(obj);}if(Object.prototype.toString.call(obj)==="[object RegExp]"){returnnewRegExp(obj);}if(Object.prototype.toString.call(obj)==="[object Undefined]"){returnnewError(obj);}// 判断对象是类let newObj = Array.isArray(obj)?[]:{}for(let item in obj){if(typeof obj[item]==='object'){
newObj[item]=deepCopy(obj[item])}else{
newObj[item]= obj[item]}}return newObj
};const newFoo =deepCopy(foo)
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 18 } } { name: '绝绝子', info: { age: 18 } }
foo.info.age =25
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 25 } } { name: '绝绝子', info: { age: 18 } }
3、第三方库lodash
第三方库lodash提供了深拷贝的功能,使用起来非常简单方便。通常只需要调用其中的一个函数,传入要拷贝的对象即可完成深拷贝操作。
const _ =require('lodash')const newFoo = _.cloneDeep(foo);
console.log(foo, newFoo);// { name: '绝绝子', info: { age: 18 } } { name: '绝绝子', info: { age: 18 } }
foo.info.age =25
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 25 } } { name: '绝绝子', info: { age: 18 } }
4、Object.assgin
注意:只有当对象中没有嵌套对象时,才可以实现深拷贝
// 对象中没有嵌套对象时const foo1 ={name:'绝绝子',age:18}const newFoo1 = Object.assign({}, foo1)
console.log(foo1, newFoo1)// { name: '绝绝子', age: 18 } { name: '绝绝子', age: 18 }
foo1.age =25
console.log(foo1, newFoo1)// { name: '绝绝子', age: 25 } { name: '绝绝子', age: 18 }// 对象中有内嵌的对象时const newFoo2 = Object.assign({}, foo)
console.log(foo, newFoo2);// { name: '绝绝子', info: { age: 18 } } { name: '绝绝子', info: { age: 18 } }
foo.info.age =25
console.log(foo, newFoo2)// { name: '绝绝子', info: { age: 25 } } { name: '绝绝子', info: { age: 25 } }
5、structuredClone
该方法为Web最新的 API,存在兼容问题。
存在的限制: 如果 structuredClone() 传入一个类的实例,会得到一个普通的对象,因为结构化拷贝会丢弃对象的原型链;如果对象包含了函数,会抛出异常;拷贝 DOM 节点会抛出异常。
const newFoo =structuredClone(foo)
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 18 } } { name: '绝绝子', info: { age: 18 } }
foo.info.age =25
console.log(foo, newFoo)// { name: '绝绝子', info: { age: 25 } } { name: '绝绝子', info: { age: 18 } }
PS:关于JavaScript实现深拷贝的方法暂时先收集这么多啦,后续有其他的再进行补充更新~
版权归原作者 日常保护好头发 所有, 如有侵权,请联系我们删除。