您现在的位置:首页 >> 前端 >> 内容

浅拷贝和深拷贝的区别,以及实现的方法详解

时间:2018/3/23 11:20:41 点击:

  核心提示:两者的区别:一个对象浅复制后,是深层次的对象地址的复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会发生改变,而深复制的则是开辟了一个新的...

两者的区别:一个对象浅复制后,是深层次的对象地址的复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另一个对象的属性也会发生改变,而深复制的则是开辟了一个新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

话不多说,上代码:

// 原始对象
var obj = { 
	a:1, 
	arr: [2,3],
	say:function(){
		console.log('hello')
	},
	obj1:{
		arr:[34,55,5],
		hand:function(){
			console.log('hand')
		},
		obj3:{
			a:1,
			take:function(){
				console.log('take')
			}
		}
	}
};

// 开始浅复制
var shallowObj = shallowCopy(obj);

// 定义浅复制逻辑
function shallowCopy(src) {
  var dst = {};
  for (var prop in src) {
    if (src.hasOwnProperty(prop)) {
      dst[prop] = src[prop];
    }
  }
  return dst;
}

// 改变复制后的新对象属性值(第一层属性)
shallowObj.a = 2; 
shallowObj.arr = [9,8];
shallowObj.say = function(){
	console.log('world')
}

// 打印新对象的及方法
console.dir(shallowObj) 
shallowObj.say(); // world

// 打印原对象及方法
console.dir(obj);
obj.say(); // hello

结果如图所示:

浅拷贝和深拷贝的区别,以及实现的方法详解

结论 : 修改新对象的属性值,第一层的属性值的确没有变化,重点来了,我们给第二层以及更深层次的属性复制试试

// 原始对象
var obj = { 
	a:1, 
	arr: [2,3],
	say:function(){
		console.log('hello')
	},
	obj1:{
		arr:[34,55,5],
		hand:function(){
			console.log('hand')
		},
		obj3:{
			a:1,
			take:function(){
				console.log('take')
			}
		}
	}
};

// 开始浅复制
var shallowObj = shallowCopy(obj);

// 定义浅复制逻辑
function shallowCopy(src) {
  var dst = {};
  for (var prop in src) {
    if (src.hasOwnProperty(prop)) {
      dst[prop] = src[prop];
    }
  }
  return dst;
}

// 改变复制后的新对象的属性值(第二层以及更深层次)
shallowObj.obj1.obj3.take = function(){
	console.log('shallowObj_take')
}
shallowObj.obj1.hand = function(){
	console.log('shallowObj_hand')
}

// 打印新对象的方法调用
shallowObj.obj1.obj3.take(); // shallowObj_take
shallowObj.obj1.hand();   // shallowObj_hand

// 打印原对象的方法调用
obj.obj1.obj3.take(); // shallowObj_take
obj.obj1.hand();  // shallowObj_hand

问题出现了:原对象的方法被新对象的修改,而产生变化。

原因是复制的是对象的地址指针,两个属性共同指向一个对象,只要其一发生变化,另一个也随之变化

深拷贝的方法:

1.可以递归递归去复制所有层级属性

// 原始对象
var obj = { 
	a:1, 
	arr: [2,3],
	say:function(){
		console.log('hello')
	},
	obj1:{
		arr:[34,55,5],
		hand:function(){
			console.log('hand')
		},
		obj3:{
			a:1,
			take:function(){
				console.log('take')
			}
		}
	}
};

// 深复制逻辑(递归调用)
function deepClone(obj){
  let objClone = Array.isArray(obj)[]:{};
  if(obj && typeof obj==="object"){
    for(key in obj){
      if(obj.hasOwnProperty(key)){
        //判断obj子元素是否为对象,如果是,递归复制
        if(obj[key] && typeof obj[key] ==="object"){
          objClone[key] = deepClone(obj[key]);
        }else{
          //如果不是,简单复制
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
} 

// 开始深复制
var shallowObj = deepClone(obj);

// 改变复制后的新对象的属性值(第二层以及更深层次)
shallowObj.obj1.obj3.take = function(){
	console.log('shallowObj_take')
}
shallowObj.obj1.hand = function(){
	console.log('shallowObj_hand')
}

shallowObj.obj1.obj3.take(); // shallowObj_take
shallowObj.obj1.hand();   // shallowObj_hand

obj.obj1.obj3.take(); // take
obj.obj1.hand();  // hand

结论:深拷贝后改变对象的属性值,不会影响原始对象的值。

2.除了递归,我们还可以借用JSON对象的parse和stringify

function deepClone(obj){
    let _obj = JSON.stringify(obj),
            objClone = JSON.parse(_obj);
    return objClone
}    
let a=[0,1,[2,3],4],
    b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

3.除了上面两种方法之外,我们还可以借用JQ的extend方法

$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝

targetObject类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1objectN可选。 Object类型 第一个以及第N个被合并的对象。


let a=[0,1,[2,3],4],
    b=$.extend(true,[],a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

作者:网络 来源:前端小贤