js对象浅拷贝与深拷贝

276 阅读4分钟

在js中,如果一个对象,作为变量赋值给另一个对象,那么两个对象得值会是相同得引用地址,其中一个改变,另外一个也会随之改变。

`

var obj1 = {
    num: 123
}
var obj2 = obj1;
obj2.num = 456;
console.log(obj1.num);  // 输出: 456

` 在我们日常开发过程当中,我们去复制一个对象得目的是为了去获得该对象的值,我们对获得的值进行操作,不会影响原对象才对。而我们要解决这样的问题,就涉及到了使用对象的浅拷贝和深拷贝来解决问题了。

浅拷贝

浅拷贝是我们日常开发使用最多的,因为他能解决我们日常开发中绝大部分的问题,首先最常用的方法就是:Object.assign(),是ES6:Object里的新增方法,Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),那么如何使用呢,请看案例:

`

var obj1 = {
    num: 123
}
var obj2 = Object.assign({}, obj1);
obj2.num = 456;
console.log(obj1.num, obj2.num);  // 输出: 123  456

`

使用方法,还是非常简单的,我们也可以使用扩展运算符...的方法来实现浅拷贝: `

var obj1 = {
    num: 123
}
var obj2 = {...obj1};
obj2.num = 456;
console.log(obj1.num, obj2.num);  // 输出: 123  456

` 所谓浅拷贝,实际上指的就是复制对象本身可枚举的属性,不会拷贝到继承属性和可本身可枚举属性的值也是对象里的值。

深拷贝

上面那句话的后半段里看上去有点绕,这里在讲什么是深拷贝,你就立马理解了。而 这个字我个人觉得就挺通俗易懂了,说白了就是更深层次的拷贝呗。那么怎么就是深拷贝呢?看案例: `

var obj1 = {
    num: 123,
    child: {
        str: '子级对象'
    }
}
var obj2 = Object.assign({}, obj1);
obj2.child.str = '子对象的str值';
console.log(obj1.child.str);  // 输出: 子对象的str值

`

通过上面的案例,我们发现,我们用了浅拷贝,但是obj1obj2里面的child子对象还是共用了一个引用地址,这就是我上面说的那句“本身可枚举属性里的值也是对象”,所以我们就需要更深层次的拷贝到这个对象里来呗,这就需要使用深拷贝了,最常见的深拷贝方法就是:JSON.parse(JSON.stringify(obj1)),下面看案例:

`

var obj1 = {
    num: 123,
    child: {
        str: '子级对象'
    }
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.child.str = '子对象的str值';
console.log(obj1.child.str);  // 输出: 子级对象

`

通过使用JSON.parse(JSON.stringify(obj1))我们实现了深拷贝,其实该方法的实现原理非常的简单,JSON.stringify()是把我们的对象转换成了JSON字符串,字符串属于基本类型了,已经不存在引用地址了,然后我们在用JSON,pase()把它转换回来,此时它已经是一个新对象了,和原对象没有任何关系了。通过这样转换的方式实现了深拷贝。但是呢,该方法也存在一定的局限性,或者说是不足:

  • 1.会忽略 undefined,
  • 2.不能序列化函数,
  • 3.不能解决循环引用的对象

下面看案例:

`

let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3,
    },
}

obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

`

如果你的对象像这样的相互引用赋值的话,那么你会发现不能使用JSON.parse(JSON.stringify(obj1))来实现了,并且还报错:

不仅如此,在遇到undefined和函数时,也不能正常实现深拷贝:

`

let obj1 = {
    id: undefined,
    fn: function() {},
    tel: 13880089009
}
let obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2) // {tel: 13880089009}

`

这样只拷贝到了tel一个属性值:那么这个时候就会用到常用得另一种深拷贝方式了:for in

`

let obj1 = {
	    id: undefined,
		fn: function() {},
		tel: 13880089009
	}
let obj2 = {}
    for (let key in obj1) {
		obj2[key] = obj1[key]
	}
    console.log(obj2)   // {id: undefined, fn: ƒ, tel: 13880089009}

`

for in本身是一个比较耗性能得循环方法,它实现得方式也简单粗暴,就是一个属性一个属性得赋值,for in还可以遍历到对象继承得所有属性和方法,正是因为这一点,所以for in是个耗性能得主。

总结

本篇章主要讲解得是浅拷贝和深拷贝这样得一个概念,并给出了个人用得一些浅拷贝和深拷贝得方法,用于对我们日常开发当中注意得一些细节操作。喜欢得朋友给个赞吧,谢谢。