js中对象的拷贝非常重要,如果理解不深入必将写出致命的bug,本文将总结之前项目开中中用到的所有拷贝方法进行总结。
由于js创建对象进内存的机制,原始数据类型 Undefined,Null,Boolean,Number、String 五类是直接进栈保存的,因此对这五种类型的数据赋值操作是内存里直接复制结果值保存,而object类型的数据都是引用数据类型,栈内保存的是地址引用,实际值存储在堆中,而对引用类型的赋值操作实际上是将栈内的引用地址拷贝赋值,因此如果具有相同引用地址的变量修改了对象的某个属性值,则会引起其他相同引用地址对象的改变,造成和预期背离的结果。
举例说明:
//定义一个员工个人对象
let obj = {
name:"cc",
age:30,
job:{type:"porgrammer",com:"ali"},
cars:["passat","bmw"]
}
//该函数只获取员工的车辆基本信息姓名和车辆
let getPerInf = function(obj){
var newObj = obj;
if(newObj){
if(newObj.age){
delete newObj.age;
}
if(ewObj.job){
delete newObj.job;
}
}
return newObj;
}
//打印函数处理后的newobj结果和原始obj结果
console.log(getPerInf(obj));
console.log(obj);
通过截图我们发现经过函数getperInf处理删除job和age属性之后对全局定义的obj对象也同样起了作用,加入此时别的函数需要完整的obj怎么办?
所以以上就必然催生对象的拷贝问题了。
浅拷贝的概念是相对于深拷贝而言的,仅仅将原对象的的第一层属性进行了赋值神并生成新对象,其本质是对象的结构决定的,比如上例中我们定义的obj对象。
let obj = {
name:"cc",
age:30,
job:{type:"porgrammer",com:"ali"},
cars:["passat","bmw"]
}
obj中即有原属数据类型属性name和age同时还有引用数据类型job和cars如果用浅拷贝实现上面需求如下:
//定义一个员工个人对象
let obj = {
name:"cc",
age:30,
job:{type:"porgrammer",com:"ali"},
cars:["passat","bmw"]
}
//该函数只获取员工的车辆基本信息姓名和车辆
let getPerInf = function(obj){
var newObj = shshallowClone(obj);
if(newObj){
if(newObj.age){
delete newObj.age;
}
if(newObj.job){
delete newObj.job;
}
}
return newObj;
}
//浅拷贝es实现
let shshallowClone = function(obj){
let newObj =new Object();
if(obj){
for(key in obj){
newObj[key] = obj[key];
}
}
return newObj;
}
console.log(obj);
console.log(getPerInf(obj));
console.log(obj);
通过浅拷贝我们发现getPerInf方法达到了我们想要的数据结果同时全局的obj对象没有受到影响。
但是这样依然会有问题,假如我们在getPerInf方法中修改job对象中com值为“360”,再在全局中修改obj对象job对象中tyep值为“managet”,我们期待的结果肯定是getPerInf方法中返回的newObj对象不会受全局obj修改job.type的影响,同时全局obj对象也不受newObj中job.com
修改的影响:
//定义一个员工个人对象
let obj = {
name:"cc",
age:30,
job:{type:"porgrammer",com:"ali"},
cars:["passat","bmw"]
}
//该函数只获取员工的车辆基本信息姓名和车辆
let getPerInf = function(obj){
var newObj = shshallowClone(obj);
if(newObj){
if(newObj.age){
delete newObj.age;
}
if(newObj.job){
// delete newObj.job;
newObj.job.com = '360'
}
}
return newObj;
}
//浅拷贝es实现
let shshallowClone = function(obj){
let newObj =new Object();
if(obj){
for(key in obj){
newObj[key] = obj[key];
}
}
return newObj;
}
console.log(obj);
console.log(getPerInf(obj));
if(obj.job){
obj.job.type = 'manager';
}
console.log(obj);
结果:
很不幸两者竟然是相同的,这就说明obj.job对象的引用与obj.job对象的引用是完全相同的。
要解决上面的问题我们就必须进行深拷贝。
深拷贝顾名思义就是将原对象的所有属性包含子属性为引用属性的对象进行复制给新对象。目的就是彻底阻断与原来对象的地址引用,实现互不影响。
//定义一个员工个人对象
let obj = {
name:"cc",
age:30,
job:{type:"porgrammer",com:"ali"},
cars:["passat","bmw"]
}
//该函数只获取员工的车辆基本信息姓名和车辆
let getPerInf = function(obj){
// var newObj = shshallowClone(obj);
let newObj = deepClone(obj)
if(newObj){
if(newObj.age){
delete newObj.age;
}
if(newObj.job){
// delete newObj.job;
newObj.job.com = '360'
}
}
return newObj;
}
//深拷贝
let deepClone = function (obj){
let newObj = (obj instanceof Array)? new Array() : new Object();
if(obj){
for(let key in obj){
if(typeof(obj[key]) === "object"){
newObj[key] = deepClone(obj[key]);
}else{
newObj[key] = obj[key];
}
}
}
return newObj;
}
console.log(obj);
console.log(getPerInf(obj));
if(obj.job){
obj.job.type = 'manager';
}
console.log(obj);
这样我们就彻底实现了对象的所有属性拷贝,obj 与 newObj 两者之间任何操作都会会不影响。
以上我们已经将对象拷贝的原因本质及浅拷贝深拷贝的区别已经详细的予以说明,接下来我们罗列一下一些常用的经典对象深浅拷贝实现方法。
浅拷贝
//浅拷贝es实现
let shshallowClone = function(obj){
let newObj =new Object();
if(obj){
for(key in obj){
newObj[key] = obj[key];
}
}
return newObj;
}
深拷贝
let deepClone = function (obj){
let newObj = (obj instanceof Array)? new Array() : new Object();
if(obj){
for(let key in obj){
if(typeof(obj[key]) === "object"){
newObj[key] = deepClone(obj[key]);
}else{
newObj[key] = obj[key];
}
}
}
return newObj;
}