JavaScript对象深拷贝

引言

在JavaScript中对对象拷贝复制通常是使用循环遍历的方式:

var obj = {
	x:1,
	y:[1,2,3]
}

var obj2 = {};
for(let key in obj){
	obj2[key] = obj[key];
}

obj.x = 10;
obj.y[0] = 4;
console.log(obj,obj2);

深度拷贝 javascript 深度拷贝对象 js_深拷贝


虽然obj2和obj输出的结果看起来是一样的,但当obj.y发生改变,obj2也会改变,因为引用类型赋值的是地址,这种复制方式一般称为浅拷贝。那么如何实现深度拷贝呢?下面介绍几种JavaScript中常见的深拷贝的方式。

一、序列化、反序列化

通过JSON.parse和JSON.stringify

var obj = {
	x:1,
	y:[1,2,3]
}

var obj2 = JSON.parse(JSON.stringify(obj)); // 先转为json字符串,再转回对象

obj.x = 10;
obj.y[0] = 4;
console.log(obj,obj2);

深度拷贝 javascript 深度拷贝对象 js_JSON_02


但是通过JSON复制有一定的局限性,对象中的值value的数据类型为undefined、function和symbol时直接被忽略。

var obj = {
	a:1,
	b:[1,2,3],
	c:undefined,
	d:function (){
		console.log(1);
	},
	e:Symbol()
}
var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj,obj2);

深度拷贝 javascript 深度拷贝对象 js_JavaScript对象深拷贝_03

二、递归实现深拷贝

面试题:深拷贝如何实现?

// 定义判断数据类型的函数
function getClass(obj){
	return Object.prototype.toString.call(obj).slice(8,-1);
}

function deepCopy(obj){
	var result,
		oclass = getClass(obj);
	// 判断传入obj是否是引用类型
	if(oclass == 'Object') result = {};
	else if(oclass == 'Array') 	result = [];
	else return obj;
	// 遍历对象
	for(let key in obj){ 
		var copy = obj[key];
		if(getClass(copy) == 'Object'||getClass(copy) == 'Array'){
			result[key] = deepCopy(copy); // 再次调用函数
			// result[key] = arguments.callee(copy);
		} else {
			result[key] = copy;
		}
	}

	return result;
}
var obj = {
	a:1,
	b:[1,2,3],
	c:undefined,
	d:function (){
		console.log(1);
	},
	e:Symbol()
}
var obj2 = deepCopy(obj);

obj.a = 10;
obj.b[0] = 4;
console.log(obj,obj2);

深度拷贝 javascript 深度拷贝对象 js_JSON_04

三、通过使用第三方工具库–jQuery

var obj = {
	a:1,
	b:[1,2,3],
	c:undefined,
	d:function (){
		console.log(1);
	},
	e:Symbol()
}

var obj2 = $.extend(true,{},obj); 
// jQuery.extend([deep],target,object1[,objectN])
// true设置为深拷贝,false是默认值为浅拷贝

obj.a = 10;
obj.b[0] = 4;
console.log(obj,obj2);

深度拷贝 javascript 深度拷贝对象 js_深拷贝_05

四、通过第三方工具库–lodash

使用_.cloneDeep(value)函数,它会递归拷贝value(深拷贝)

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects); // 返回拷贝后的值
console.log(deep[0] === objects[0]);
// => false

五、使用immutable.js-----性能最高

六、通过Object.create()/Object.assign()–浅拷贝

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

var obj = {
    a: 1,
    b: [1, 2, 3],
    c: undefined,
    d: function () {
        console.log(1);
    },
    e: Symbol()
}

var obj2 = Object.create(obj); // 使用继承的方式
// 同理,使用Object.assign()
var obj3 = {};
Object.assign(obj3, obj);

obj.a = 10;
obj.b[0] = 4;

console.log(obj, obj2, obj3);

深度拷贝 javascript 深度拷贝对象 js_JSON_06


但是这两种方法都只对一层的对象有效